为什么从我的 React 对象复制状态时我必须将我的对象展开两次

Why do I have to spread my object twice when copying state from my react object

我在下面有这个对象,我想测试一下当我执行一个操作来更改 group 键的状态时它是否正确更新。如您所见,此对象中的键值对比 group 多得多,所以我想传播 initialState 对象并编写我的测试,仅更改 group 值。

这是初始状态:

export const initialState = {
  testing: false,
  filters: {
    start: moment()
      .startOf("month")
      .format(),
    end: moment()
      .endOf("month")
      .format(),
    city: "",
    group: "daily"
  },
  locations: []
};

我的单元测试:

test("It performs the setGroup action correctly", () => {
    const active = "month";

    const action = setGroup(active);

    const state = reducer(
      {
        ...initialState,
        filters: {
          ...initialState.filters,
          group: "daily"
        }
      },
      action
    );

    expect(state).toEqual({
      ...initialState,
      filters: {
        ...initialState.filters,
        group: "month"
      }
    });
  });

这个测试有效,但是我想知道为什么我必须传播 initialstate 一次,然后在过滤器对象中再次传播 initialState.filters 只是为了操纵月份。

在我看来这应该没问题:

const state = reducer(
      {
        ...initialState,
        filters: {
          group: "daily"
        }
      },
      action
    );

我的问题是为什么我需要放置 initialState.filters 行才能使该测试可行。请有人能帮我理解吗?我做了研究,但找不到任何东西。

JavaScript 的展开运算符相当愚蠢;它不会像某些图书馆提供的那样尝试进行任何类型的智能合并。当您添加 filters 属性时,JavaScript 会简单地覆盖 initialState 的传播 filters 定义。

您可以认为您的代码示例大致等同于

Object.assign(
  {},
  initialState,
  {
    filters: { ... }
  })

当您执行 ...initialState.filters 时,您正在 initialState 上传播 filters 对象。您必须这样做,因为您刚刚在 reducer 中定义了一个新的 filters 对象,因此必须在其中放置您希望从旧版本中保留的值。

如果这一切看起来有点冗长,请查看 Immer,它允许您使用更传统的 API

来拥有不可变对象

https://immerjs.github.io/immer/docs/introduction