65.9K
CodeProject 正在变化。 阅读更多。
Home

为什么不应该直接修改 React 状态

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018 年 6 月 2 日

CPOL

2分钟阅读

viewsIcon

11679

每个人都说不要这样做。永远不要直接修改状态,总是调用 setState。但为什么呢?

每个人都说不要这样做永远不要直接修改状态,总是调用setState

但为什么呢?

如果你尝试过,你可能注意到没有发生什么坏事。如果你直接修改状态,调用this.setState({})甚至this.forceUpdate(),那么一切可能看起来都很好。

this.state.cart.push(item.id);
this.setState({ cart: this.state.cart });
// renders like normal! maybe?

这是一个坏主意,原因有两个(即使在示例以及许多其他情况下,它确实有效)。

(其他要避免的模式包括 this.state.something = xthis.state = x)

直接修改状态会导致奇怪的 bug,以及难以优化的组件。这里有一个例子。

如你所知,优化 React 组件性能的一种常见方法是使其“纯净”,这会导致它仅在 props 更改时才重新渲染(而不是每次其父组件重新渲染时)。这可以通过扩展 React.PureComponent 而不是 React.Component 来自动完成,或者通过手动实现 shouldComponentUpdate 生命周期方法来比较 nextProps 与当前 props。如果 props 看起来相同,它将跳过渲染,并节省一些时间。

这里有一个简单的组件,它渲染一个项目列表(请注意,它扩展了 React.PureComponent

class ItemList extends React.PureComponent {
  render() {
    return (
      <ul>
        {this.props.items.map(item => <li key={item.id}>{item.value}</li>)}
      </ul>
    );
  }
}

现在,这里有一个小型应用程序,它渲染 ItemList 并允许你将项目添加到列表中——以正确的方式(不可变的方式),以及错误的方式(通过修改状态)。请注意会发生什么。

class App extends Component {
  // Initialize items to an empty array
  state = {
    items: []
  };

  // Initialize a counter that will increment
  // for each item ID
  nextItemId = 0;

  makeItem() {
    // Create a new ID and use
    // a random number as the value
    return {
      id: this.nextItemId++,
      value: Math.random()
    };
  }

  // The Right Way:
  // copy the existing items and add a new one
  addItemImmutably = () => {
    this.setState({
      items: [...this.state.items, this.makeItem()]
    });
  };

  // The Wrong Way:
  // mutate items and set it back
  addItemMutably = () => {
    this.state.items.push(this.makeItem());
    this.setState({ items: this.state.items });
  };

  render() {
    return (
      <div>
        <button onClick={this.addItemImmutably}>
          Add item immutably (good)
        </button>
        <button onClick={this.addItemMutably}>Add item mutably (bad)</button>
        <ItemList items={this.state.items} />
      </div>
    );
  }
}

试试看!

单击 immutable 添加按钮几次,并注意列表按预期更新。

然后单击 mutable 添加按钮,并注意即使状态正在更改,新项目也不会出现。

最后,再次单击 immutable 添加按钮,并观察 ItemList 如何使用所有丢失的(可变添加的)项目重新渲染。

这是因为 ItemList 是纯净的,并且将新项目推送到 this.state.items 数组不会替换底层数组。当 ItemList 被要求重新渲染时,它会注意到其 props 没有更改,因此它不会重新渲染。

回顾

所以这就是为什么你不应该修改状态的原因,即使你立即调用 setState。优化的组件如果这样做可能不会重新渲染,并且渲染 bug 将难以追踪。

相反,在调用 setState 时,始终创建新的对象和数组,就像我们上面使用展开运算符所做的那样。了解更多关于如何使用展开运算符进行不可变更新

为什么不应该直接修改 React 状态 最初由 Dave Ceddia 于 2018 年 6 月 01 日在 Dave Ceddia 上发布。

© . All rights reserved.