为什么不应该直接修改 React 状态
每个人都说不要这样做。永远不要直接修改状态,总是调用 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 = x
和 this.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 上发布。