This article mainly introduces three points that need to be paid attention to when using React.setState. It puts forward three points that are easy to ignore for React novices. It has certain reference value. Interested friends can For reference
Preface
The original title of this article is 3 Reasons why I stopped using React.setState, but I am not very interested in the arguments put forward by the original author, but The three points raised by the author are easy to ignore for React novices, so I will only mention part of the content here, and change the title to three points that need to be paid attention to when using React.setState.
Text
For React novices, using setState is a very complicated thing. Even if you are skilled in React development, it is very likely that some bugs will occur due to some mechanisms of React, such as the following example:
The document also explains when using setState When doing this, what should you pay attention to:
Note:
Never change this.state directly, because calling setState() later may replace the changes you made
Change. Treat this.state as immutable.
setState() does not change this.state immediately, but creates a state transition that will be processed soon. Accessing this.state after calling this method may return the existing value.
There are no synchronization guarantees for calls to setState, and calls may be batched for performance gains.
setState() will always trigger a redraw unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are used and this logic cannot be implemented in shouldComponentUpdate(), calling setState() only when there is a difference between the new state and the previous state can avoid unnecessary re-rendering.
To sum up, when using setState, there are three issues to pay attention to:
1. setState is asynchronous (Translator's Note: Synchronization is not guaranteed)
Many developers did not notice that setState is asynchronous at first. If you modify some state and then view it directly, you will see the previous state. This is the most error-prone place in setState. The word setState doesn't look asynchronous, so it can cause bugs if you use it without thinking. The following example illustrates this problem well:
class Select extends React.Component { constructor(props, context) { super(props, context) this.state = { selection: props.values[0] }; } render() { return ( <ul onKeyDown={this.onKeyDown} tabIndex={0}> {this.props.values.map(value => <li className={value === this.state.selection ? 'selected' : ''} key={value} onClick={() => this.onSelect(value)} > {value} </li> )} </ul> ) } onSelect(value) { this.setState({ selection: value }) this.fireOnSelect() } onKeyDown = (e) => { const {values} = this.props const idx = values.indexOf(this.state.selection) if (e.keyCode === 38 && idx > 0) { /* up */ this.setState({ selection: values[idx - 1] }) } else if (e.keyCode === 40 && idx < values.length -1) { /* down */ this.setState({ selection: values[idx + 1] }) } this.fireOnSelect() } fireOnSelect() { if (typeof this.props.onSelect === "function") this.props.onSelect(this.state.selection) /* not what you expected..*/ } } ReactDOM.render( <Select values={["State.", "Should.", "Be.", "Synchronous."]} onSelect={value => console.log(value)} />, document.getElementById("app") )
At first glance, there seems to be nothing wrong with this code. The onSelect method is called in both event handlers. However, there is a bug in this Select component that nicely illustrates the previous GIF. The onSelect method always passes the previous state.selection value, because when fireOnSelect is called, setState has not completed its work. I think React should at least change setState to scheduleState or make the callback function a required parameter.
This bug is easy to fix. The hardest part is that you need to know that there is this problem.
2. setState will cause unnecessary rendering
The second problem caused by setState is: each call will cause re-rendering. Many times, these re-renders are unnecessary. You can use printWasted in the React performance tools to see when unnecessary rendering occurs. However, roughly speaking, there are several reasons for unnecessary rendering:
The new state is actually the same as the previous one. This problem can usually be solved with shouldComponentUpdate. You can also use pure render or other libraries to solve this problem.
Usually the changed state is related to rendering, but there are exceptions. For example, some data is displayed based on certain states.
Third, some states have nothing to do with rendering. Some state may be related to events and timer IDs.
3.setState cannot effectively manage all component states
Based on the last point above, not all component states are SetState should be used to save and update. Complex components may have a variety of states that need to be managed. Using setState to manage these states will not only cause a lot of unnecessary re-rendering, but also cause related life cycle hooks to be called all the time, causing many strange problems.
Afterword
In the original article, the author recommended a library called MobX to manage some states. I am not very cold, so I will not introduce it. If you are interested, you can read the introduction in the original article through the link at the top.
Based on the three points raised above, I think newbies should pay attention to the following:
setState is not guaranteed to be synchronized
setState is not guaranteed to be synchronized, it is not guaranteed Synchronization is not guaranteed. Say important things three times. The reason why it is not said to be asynchronous is because setState is also updated synchronously in some cases. You can refer to this article
If you need to obtain the modified value directly after setState, there are several options:
Pass in the corresponding parameters and do not obtain them through this.state
针对于之前的例子,完全可以在调用 fireOnSelect 的时候,传入需要的值。而不是在方法中在通过 this.state 来获取
使用回调函数
setState 方法接收一个 function 作为回调函数。这个回掉函数会在 setState 完成以后直接调用,这样就可以获取最新的 state 。对于之前的例子,就可以这样:
this.setState({ selection: value }, this.fireOnSelect)
使用setTimeout
在 setState 使用 setTimeout 来让 setState 先完成以后再执行里面内容。这样子:
this.setState({ selection: value }); setTimeout(this.fireOnSelect, 0);
直接输出,回调函数, setTimeout 对比
componentDidMount(){ this.setState({val: this.state.val + 1}, ()=>{ console.log("In callback " + this.state.val); }); console.log("Direct call " + this.state.val); setTimeout(()=>{ console.log("begin of setTimeout" + this.state.val); this.setState({val: this.state.val + 1}, ()=>{ console.log("setTimeout setState callback " + this.state.val); }); setTimeout(()=>{ console.log("setTimeout of settimeout " + this.state.val); }, 0); console.log("end of setTimeout " + this.state.val); }, 0); }
如果val默认为0, 输入的结果是:
Direct call 0
In callback 1
begin of setTimeout 1
setTimeout setState callback 2
end of setTimeout 2
setTimeout of settimeout 2
和渲染无关的状态尽量不要放在 state 中来管理
通常 state 中只来管理和渲染有关的状态 ,从而保证 setState 改变的状态都是和渲染有关的状态。这样子就可以避免不必要的重复渲染。其他和渲染无关的状态,可以直接以属性的形式保存在组件中,在需要的时候调用和改变,不会造成渲染。
避免不必要的修改,当 state 的值没有发生改变的时候,尽量不要使用 setState 。虽然 shouldComponentUpdate 和 PureComponent 可以避免不必要的重复渲染,但是还是增加了一层 shallowEqual 的调用,造成多余的浪费。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
The above is the detailed content of What should you pay attention to when using React.setState?. For more information, please follow other related articles on the PHP Chinese website!