深度更新React状态(数组),没有不可变,没有缺点



我知道使用不可变是一种深入更新React状态的好方法,但我想知道这种方法是否有任何我没有看到的缺点:

假设this.state.members的形状为Array<MemberType>,其中MemberType{ userId: String, role: String }

如果用户修改了其他用户的角色,则执行如下方法:

  changeMemberRole = (userId, event, key, value) => {
    const memberIndex = _findIndex(this.state.members,
      (member) => member.userId === userId);
    if (memberIndex >= 0) {
      const newMembers = [...this.state.members];
      newMembers[memberIndex].role = value;
      this.setState({ members: newMembers });
    }
  };

用Immutable的setIn替换它会有什么好处,除了可能更简洁的语法吗?

使用或不使用Immutable.js的区别当然是不变性¹:)

当你声明const newMembers = [...this.state.members]时,你正在复制一个引用数组,这确实是一个新数组(通过索引修改直接子数组,如0, 1, 2不会反映出来),但填充了相同的对象引用,因此内部/深层更改是共享的。这被称为一个浅拷贝

newMembers are not so new

因此,对newMembers元素的任何更改也会在相应的this.state.members元素中进行。对于您的示例来说,这很好,到目前为止还没有真正的优势。

那么,为什么是不变性?

它真正的好处不容易在小片段中观察到,因为它更多的是关于心态。摘自Immutable.js主页:

使应用程序开发困难的大部分是跟踪突变和维持状态。使用不可变数据进行开发鼓励您以不同的方式思考数据如何流经您的应用程序。

不可变性带来了许多函数式范式的好处,如避免副作用或竞态条件,因为您将变量视为值而不是对象,从而使其更容易理解其范围和生命周期,从而最大限度地减少bug。

react的一个特殊优点是,当shouldComponentUpdate发生突变时,可以安全地检查状态的变化:

// assume this.props.value is { foo: 'bar' }
// assume nextProps.value is { foo: 'bar' },
// but this reference is different to this.props.value
this.props.value !== nextProps.value; // true

当使用对象而不是值时,nextPropsthis.props.value将被视为不同的引用(除非我们执行深度比较)并触发重新渲染,这在规模上可能会非常昂贵。

¹除非你在模拟自己的不变性,否则我更信任Immutable.js

你没有复制角色,因此如果你的一个组件将角色作为道具(如果有的话)不能利用纯渲染优化(覆盖shouldComponentUpdate并检测道具何时被实际更改)。

但是由于您可以在没有immutablejs的情况下复制角色,因此除了您必须输入更多(从而有更多犯错的机会)之外,没有任何有效的区别。这本身就是一个巨大的缺点,降低了你的工作效率。

From setIn docs:

返回一个新的Map,该Map在此keyPath上具有设置值。如果keyPath中的任何键不存在,则将在该键处创建一个新的不可变映射。

这可能不是您想要的,因为如果给定的角色不存在,您可能不想为其插入新成员。这归结为您是否能够控制在函数中传递的userId参数,并事先验证它是否存在。

这个解决方案很好。如果你愿意,你可以用update代替它

最新更新