关于React中setState的理解

我们先来看一个问题

class Test extends React.Component {
constructor(props) {

super(props);
this.state = {
  num: 0,
};

}
componentWillMount() {

this.setState({
  num: this.state.num + 1,
});
console.log(this.state.num);
this.setState({
  num: this.state.num + 1,
});
console.log(this.state.num);

}
render() {

console.log(this.state.num);
return (<div />);

}
}

export default Test;
在上面这段代码中3个console.log()分别打印出来的num是多少呢?

很多人会认为是:1 2  2,但是打印出来的是0 0 1。

所以我们可以很确定认为setState这个在React中用来更新state的方法是一个异步的方法。

setState异步更新

setState方法通过一个队列机制实现state更新,当执行setState的时候,会将需要更新的state合并之后放入状态队列,而不会立即更新this.state(可以和浏览器的事件队列类比)。

setState简化调用栈:
《关于React中setState的理解》

那么我们要如何解决上面的问题呢?

1、利用setTimeout

componentWillMount() {

setTimeout(() => {
  this.setState({
    num: this.state.num + 1,
  });
  console.log(this.state.num);  // 1
  this.setState({
    num: this.state.num + 1,
  });
  console.log(this.state.num);  // 2
}, 0);

}

2、利用setState的回调函数

在React的官方文档中:

setState(updater[, callback])
updater的函数如下

(prevState, props) => stateChange
所以我们可以通过回调去实时获取它更新的值:

componentWillMount() {

this.setState(((num) => {
   num.num++;
}), () => {
  console.log(this.state.num); // 2
});
this.setState(((num) => {
    num.num++;
}), () => {
  console.log(this.state.num); // 2
});

}

3、利用Promise,进行封装

setStatePromise(updator) {

return new Promise(((resolve, reject) => {
  this.setState(updator, resolve);
}));

}
componentWillMount() {

this.setStatePromise(({ num }) => ({
  num: num + 1,
})).then(() => {
  console.log(this.state.num);
});

}

注意事项:

1、在State更新时,如果更新的值涉及到了state和props,我需要注意在调用setState时不要直接使用this.props和this.state,在React文档中这样说到:

React may batch multiple setState() calls into a single update for performance.
// React可能会将多个setState()调用批量处理为单个更新以提高性能。
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
// 因为this.props和this.state可能会异步更新,所以不应该依赖它们的值来计算下一个状态。
那么我们怎样去解决这个问题呢?

this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
2、如果在shouldComponentUpdate或者componentWillUpdate方法中调用setState,此时this._pending-StateQueue != null,就会造成循环调用,使得浏览器内存占满后崩溃。(亲测有效)

    原文作者:Pjee
    原文地址: https://segmentfault.com/a/1190000015134073
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞