Home  >  Article  >  Web Front-end  >  What should you pay attention to when using React.setState?

What should you pay attention to when using React.setState?

亚连
亚连Original
2018-06-20 11:17:551734browse

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 ? &#39;selected&#39; : &#39;&#39;}
      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:

  1. 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.

  2. Usually the changed state is related to rendering, but there are exceptions. For example, some data is displayed based on certain states.

  3. 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 的调用,造成多余的浪费。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中使用axios实现文件上传

使用gulp如何创建完整的项目流程

在js中如何实现将数组添加到对象中

在jQuery中如何实现动态控制页面元素

在canvas中如何实现轨迹回放功能

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn