为啥 React 在直接改变状态时会更新?
Posted
技术标签:
【中文标题】为啥 React 在直接改变状态时会更新?【英文标题】:Why does React update when directly mutating state?为什么 React 在直接改变状态时会更新? 【发布时间】:2020-10-09 19:40:19 【问题描述】:在下面的示例中,我使用 ES6 Map 作为 React 中的状态值:
class App extends React.Component
constructor(props)
super(props);
const results = new Map();
results["group1"] = [ value: "..." , value: "..." ];
this.state = results ;
onUpdateClick(i)
this.state.results["group1"][i].value = i;
this.setState();
onResetClick(i)
this.state.results["group1"][i].value = "...";
this.setState();
render()
const results = this.state;
return (
<div>
results["group1"].map((r, i) => (
<div>
r.value
<button onClick=e => this.onUpdateClick(i)>update</button>
<button onClick=e => this.onResetClick(i)>reset</button>
</div>
))
</div>
);
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='container'></div>
当您单击按钮时,我会直接更新 Map,然后在不带参数的情况下调用 setState。我不制作地图的克隆/深层副本。根据我对 React 文档的理解,这应该不起作用,并且在文档中明确警告:
永远不要直接改变 this.state,因为之后调用 setState() 可能会替换你所做的改变。将 this.state 视为不可变的
(https://reactjs.org/docs/react-component.html#state)
文档还指出比较是浅的,所以用空对象调用肯定不会导致合并,因此不会重新渲染?
为什么这个例子有效?
(我还应该注意,我也使用 React v16.9.0 重现了这种行为)
编辑: 我还想指出(因为许多答案是指我传递一个空对象的事实)如果我像这样调用 setState,组件将被重新渲染(和更新):
this.setState( results: this.state.results )
这似乎不应该导致重新渲染
【问题讨论】:
您不是在没有参数的情况下调用setState
,而是给它一个空对象。 React 将尝试更新,但由于空对象不包含任何与先前状态合并的键,因此它会保留您的突变。
谢谢@BrianThompson。我还要注意: setState( results: this.state.results ) 也会导致重新渲染,在这种情况下我再次问这个问题......
供参考:github.com/facebook/react/blob/master/packages/react-reconciler/…
【参考方案1】:
来自文档:setState
setState() 总是会导致重新渲染,除非 shouldComponentUpdate() 返回 false。
这意味着无论您是否将更改作为参数传递,this.setState();
都会导致重新渲染
【讨论】:
【参考方案2】:你错了:
this.setState(); // this will do nothing.
您期望状态应该使用空对象进行更新。但事实并非如此。
当您使用 setState 方法时,它期望属性更新。但是由于您没有向 setState 提供任何属性,因此它什么也不做。但它仍会按 setState 的性质重新渲染您的组件。
更新:针对您的查询
编辑:我还想指出(因为许多答案是指我传递一个空对象的事实)如果我像这样调用 setState,组件将被重新渲染(和更新):
this.setState( results: this.state.results )
这似乎不应该导致重新渲染
React 内部声明它类似于this.state.results
,当您尝试使用完全相同的状态更新状态时。所以,没有什么可以重新渲染的。但是当你对空对象使用 setState 时,React 会刷新它的状态,所以会发生重新渲染。
阅读来自docs 的注释:
注意
避免将道具复制到状态!这是一个常见的错误:
constructor(props)
super(props);
// Don't do this!
this.state = color: props.color ;
问题在于它既没有必要(您可以直接使用this.props.color
),而且会产生错误(对颜色属性的更新不会反映在状态中)。
【讨论】:
我不希望它使用空对象进行更新 - 如上所述,我希望没有任何更新,因为没有什么可比较的。 啊,明白了。但如前所述,无论何时使用 setState,react 组件都会重新渲染,无论其变化如何。【参考方案3】:根据文档,setState() 将始终导致重新渲染,除非 shouldComponentUpdate() 返回 false。 您可以将 render() 函数视为创建 React 元素树。在下一次 state 或 props 更新时,render() 函数将返回不同的 React 元素树(Virtual DOM)。
在任何时候,React 库都会维护两个虚拟 DOM 副本。
当触发 setState() 的请求时,React 会创建一个新树,其中包含组件中的响应式元素(以及 更新后的状态)。 这棵树用于通过将组件的 UI 与前一棵树的元素进行比较来确定组件的 UI 应如何更改以响应状态变化。
虚拟 DOM 然后通过称为协调的过程与真实 DOM 同步。
至于突变,不进行直接突变可以避免项目中出现不必要的错误,因为 setState() 是异步的,这意味着您不能期望 setState 立即更新状态(它批量执行),因此任何先前的突变都可以被先前的 setState 状态更新操作覆盖。
参考-
反应文档 https://medium.com/@happymishra66/virtual-dom-in-reactjs-43a3fdb1d130 https://css-tricks.com/understanding-react-setstate/ https://www.pluralsight.com/guides/virtual-dom-difference-maker-react-js https://medium.com/coffee-and-codes/hey-react-what-is-the-virtual-dom-466ec333bf9a?source=---------1------------------ https://medium.com/@kkranthi438/dont-mutate-state-in-react-6b25d5e06f42【讨论】:
【参考方案4】:当调用this.setState
时,基本上React 会将我们传递给这个函数的参数与旧的this.state
合并以产生一个新的状态值。例如:
this.state = Object.assign(, this.state, passedParam);
所以在上面的 sn-ps 中,每次调用 this.setState();
时都会创建一个新的状态对象,这会使组件重新渲染。
【讨论】:
以上是关于为啥 React 在直接改变状态时会更新?的主要内容,如果未能解决你的问题,请参考以下文章
React 的 setState(),嵌套结构的数据变异,为啥不直接修改状态呢?
不要直接改变状态,在 React JS 中使用 setState() react/no-direct-mutation-state
为啥在类构造函数中使用 setState 方法时 React 会抛出错误?