我在这个 React Hooks Reducer 中处理状态更新吗?

Am I Handling State Updates in this React Hooks Reducer?

在下面的代码片段中,我有一个工作的减速器,我认为它是不正确的,因为它重新使用现有状态而不是替换它。它确实有效,但没有让我担心的错误。

  1. 不需要 Object.assign 是否正确?
  2. 假设不正确应该有错误吗?
  3. 有什么工具可以帮助我验证状态更新的正确性?

进入这个 reducer 的状态只是一个只有第一级对象的简单数组。也就是说,类似于:

const stateIn = [{id: 101,favorite: 0},{id: 102,favorite: 1},...]

我怀疑的减速器如下:

  const speakersReducer = (state, action) => {
    switch (action.type) {
      case "loadspeakers": {
        return action.data;
      }
      case "favorite": {
        return state.map((item, index) => {
          if (item.id === action.sessionId) {
            // let speakerToUpdate = Object.assign(item);
            // speakerToUpdate.favorite = 1;
            item.favorite = 1;
            return item;
            // return speakerToUpdate;
          }
          return item;
        });
      }
      ...
      default:
        return state;
    }
  };
  export default speakersReducer;

Is this correct?

item 的突变在 "favorite" 案例中确实是可疑的,正如@Tholle 指出的那样。 reducer 中的突变通常被认为是 anti-pattern 并且经常会导致错误。我肯定会建议将其更改为不可变模式。

但后来...

Why doesn't this cause an error, assuming it's not correct?

reducer 中的可变性是错误的常见来源,但特定的突变是否会导致错误取决于它的变异方式以及 reducer 的使用方式。

虽然收藏夹案例可能会错误地将 item object 从一个状态版本重用到另一个状态,但它总是会产生一个新的状态数组,因为 Array.prototype.map 总是 returns 一个新数组。因此,例如,只关心外部状态 object 的身份的 useReducer 将正确触发 re-render,尽管发生了突变。

另一方面,在数组中的特定项目上记忆的 redux 选择器会错误地 return 旧值。如果旧的 items 被缓存和重用,突变也可能导致意想不到的错误。但这些都是相当具体的案例,所以在大多数情况下,我怀疑这会奏效。 (不过我还是会把它改成不变异)

Any tools that would help me verify correctness for state updates?

你想要的是某种形式的不可变性:在 JS 中有许多用于不可变性的库。

  • ImmutableJS 用自己的 API 定义自己的数据结构
  • seamless-immutable 定义了看起来像普通可变数据结构的不可变数据结构
  • immer 允许您使用普通的可变操作语法执行不可变操作。

此外,Typescript 可以单独使用,也可以与这些库结合使用:允许将 objects 和数组声明为只读,并在更改它们时引发编译器错误。

linter 规则也可能有帮助,但它会很棘手,除非你同意禁止整个代码库中的所有突变,或者手动切换内部 reducer 的规则。