简体   繁体   English

为什么直接变异 state 时 React 会更新?

[英]Why does React update when directly mutating state?

In the example below I am using an ES6 Map as a state value in React:在下面的示例中,我在 React 中使用 ES6 Map 作为 state 值:

 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}&nbsp; <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>

When you click the button, I directly update the Map, in place and then call setState with no arguments.当你点击按钮时,我直接更新 Map,就地,然后调用 setState 没有 arguments。 I do not make a clone/deep copy of the map.我不制作 map 的克隆/深拷贝。 Based on my understanding of the React docs, this should not work and is explicitly warned against in the docs:根据我对 React 文档的理解,这应该不起作用,并且在文档中明确警告:

Never mutate this.state directly as calling setState() afterwards may replace the mutation you made.永远不要突变 this.state 直接调用 setState() 可能会替换您所做的突变。 Treat this.state as if it were immutable将 this.state 视为不可变

( https://reactjs.org/docs/react-component.html#state ) https://reactjs.org/docs/react-component.html#state

The docs also state that comparisons are shallow, so calling with an empty object should surely result in no merge and therefore no re-render?文档还 state 比较浅,所以用空的 object 调用应该肯定不会导致合并,因此不会重新渲染?

Why does this example work?为什么这个例子有效?

(also I should note that I also reproduce this behaviour with React v16.9.0) (我还应该注意,我也使用 React v16.9.0 重现了这种行为)

Edit: I also want to point out (since many answers refer to the fact that I am passing an empty object) that the component is re-rendered (and updated) if I call setState like so:编辑:我还想指出(因为许多答案是指我传递一个空对象的事实)如果我像这样调用 setState 组件将被重新渲染(和更新):

this.setState({ results: this.state.results })

which seems like it should not cause a re-render这似乎不应该导致重新渲染

From the documentation: setState来自文档: setState

setState() will always lead to a re-render unless shouldComponentUpdate() returns false.除非 shouldComponentUpdate() 返回 false,否则 setState() 将始终导致重新渲染。

Which means that this.setState({});这意味着this.setState({}); leads to re-render regardless if you pass a change as an argument or not无论您是否将更改作为参数传递,都会导致重新渲染

You're wrong:你错了:

this.setState({}); // this will do nothing.

You are expecting that the state should be updated with empty object.您期望 state 应更新为空 object。 But it is not.但事实并非如此。

When you use setState method, it expects properties to update.当您使用 setState 方法时,它期望属性更新。 But since you do not provide any property to setState, it will do nothing.但是由于您没有向 setState 提供任何属性,因此它什么也不做。 But it will still re-render your component by setState nature.但它仍会按 setState 的性质重新渲染您的组件。

Update: To your query更新:对您的查询

Edit: I also want to point out (since many answers refer to the fact that I am passing an empty object) that the component is re-rendered (and updated) if I call setState like so:编辑:我还想指出(因为许多答案是指我传递一个空对象的事实)如果我像这样调用 setState 组件将被重新渲染(和更新):

this.setState({ results: this.state.results })

which seems like it should not cause a re-render这似乎不应该导致重新渲染

React internally states that it is similar to this.state.results when you try to update state with exact same state.当您尝试使用完全相同的 state 更新 state 时,React 内部声明它类似于this.state.results So, there's nothing to re-render.所以,没有什么可以重新渲染的。 But when you use setState with empty object, React will flash its state so the re-render occurs.但是当你使用空的 object 的 setState 时,React 将 flash 和它的 state 发生重新渲染。

Read this note from the docs :文档中阅读此注释:

Note笔记

Avoid copying props into state: This is a common mistake:避免将 props 复制到 state 中:这是一个常见错误:

constructor(props) {
 super(props);
 // Don't do this!
 this.state = { color: props.color };
}

The problem is that it's both unnecessary (you can use this.props.color directly instead), and creates bugs (updates to the color prop won't be reflected in the state).问题是它既是不必要的(您可以直接使用this.props.color代替),并且会产生错误(对 color 道具的更新不会反映在状态中)。

According to docs, setState() will always lead to a re-render unless shouldComponentUpdate() returns false.根据文档,除非 shouldComponentUpdate() 返回 false,否则setState() 将始终导致重新渲染 You can think of the render() function as creating a tree of React elements .您可以将render() function 视为创建 React 元素树 On the next state or props update, that render() function will return a different tree of React elements( Virtual DOM ).在下一次 state 或 props 更新中,render() function 将返回不同的 React 元素树(虚拟 DOM )。

At any point in time, the React library maintains two copies of the Virtual DOM .在任何时候, React 库都会维护两个 Virtual DOM 副本

When the request to setState() is triggered, React creates a new tree containing the reactive elements in the component (along with the updated state ).触发 setState() 请求时,React 会创建一个新树,其中包含组件中的反应性元素(以及更新的 state )。 This tree is used to figure out how the component's UI should change in response to the state change by comparing it with the elements of the previous tree.此树用于通过将组件的 UI 与前一个树的元素进行比较来确定组件的 UI 应如何更改以响应 state 更改。

The Virtual DOM which is then synced with real DOM through process called reconciliation.虚拟 DOM 然后通过称为协调的过程与真实 DOM 同步。

As for mutations, not doing direct mutations would avoid unnecessary bugs in your project because setState() is asynchronous which means you cannot expect setState to update state immediately(it does in batches) so any previous mutations could be overridden by previous setState state update operation.至于突变,不进行直接突变可以避免项目中出现不必要的错误,因为setState() 是异步的,这意味着您不能期望 setState 立即更新 state(它是分批更新的),因此任何先前的突变都可以被之前的 setState state 更新操作覆盖.

Refs-参考-

When calls this.setState , basically React will merge the parameter we pass to this function with the old this.state to produce a new state value.当调用this.setState时,基本上 React 会将我们传递给这个 function 的参数与旧的this.state ,以产生一个新的 state 值。 For example:例如:

this.state = Object.assign({}, this.state, passedParam);

So in above snippets, every time you call this.setState({});所以在上面的片段中,每次你调用this.setState({}); a new state object has been created which make Component re-rendered.一个新的 state object 已创建,它使组件重新渲染。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM