setState 总结

2019-07-16  本文已影响0人  baxiamali

引子:先来提神醒脑一下:

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      secretNumber: 10
    };
  }
  
  componentDidMount() {
    this.setState({secretNumber: this.state.secretNumber + 1});
    console.log(this.state.secretNumber);    // 第 1 次 log

    this.setState({secretNumber: this.state.secretNumber + 1});
    console.log(this.state.secretNumber);    // 第 2 次 log

    setTimeout(() => {
      this.setState({secretNumber: this.state.secretNumber + 1});
      console.log(this.state.secretNumber);  // 第 3 次 log

      this.setState({secretNumber: this.state.secretNumber + 1});
      console.log(this.state.secretNumber);  // 第 4 次 log
    }, 0);
  }

  render() {
    return null;
  }
};

快写下你的答案,十行后公布答案~~~











答案(10 10 12 13)

OK 以下是知识点总结


setState的异步性

  1. 调用 setState 其实是异步的 —— 不要指望在调用 setState 之后,this.state 会立即映射为新的值。
  2. setState() 视为请求而不是立即更新组件的命令。
  3. setState() 并不总是立即更新组件。它会批量推迟更新。这使得在调用 setState() 后立即读取 this.state 成为了隐患。

典型示例:

incrementCount() {
  // 注意:这样 *不会* 像预期的那样工作。
  this.setState({count: this.state.count + 1});
}

handleSomething() {
  // 假设 `this.state.count` 从 0 开始。
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();
  // 当 React 重新渲染该组件时,`this.state.count` 会变为 1,而不是你期望的 3。

  // 这是因为上面的 `incrementCount()` 函数是从 `this.state.count` 中读取数据的,
  // 但是 React 不会更新 `this.state.count`,直到该组件被重新渲染。
  // 所以最终 `incrementCount()` 每次读取 `this.state.count` 的值都是 0,并将它设为 1。

  // 问题的修复参见下面的说明。
}

如果你需要基于当前的 state 来计算出新的值,该怎么办?

incrementCount() {
  this.setState((state) => {
    // 重要:在更新的时候读取 `state`,而不是 `this.state`。
    return {count: state.count + 1}
  });
}

handleSomething() {
  // 假设 `this.state.count` 从 0 开始。
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();

  // 如果你现在在这里读取 `this.state.count`,它还是会为 0。
  // 但是,当 React 重新渲染该组件时,它会变为 3。
}
  1. setState() 的第二个参数为可选的回调函数,它将在 setState 完成合并并重新渲染组件后执行。通常,我们建议使用 componentDidUpdate() 来代替此方式。

总结: 调用 setState() 后立即读取 this.state,更新依赖于当前的 statestate需要特别注意。需要采取updater()函数,回调函数或者 componentDidUpdate()方案处理。

setState 什么时候是异步的

  1. 目前,在事件处理函数内部的 setState 是异步的。

如果 Parent 和 Child 在同一个 click 事件中都调用了 setState ,这样就可以确保 Child 不会被重新渲染两次。取而代之的是,React 会将该 state “冲洗” 到浏览器事件结束的时候,再统一地进行更新。这种机制可以在大型应用中得到很好的性能提升。

  1. 除此之外的setState调用会同步执行this.state
    所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。

原因:在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state

setState 简化调用栈 图例

68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f32303139303231313134313432343733312e6a70673f782d6f73732d70726f636573733d696d6167652f77617465726d61726b2c747970655f5a6d46755a33706f5a57356e6147567064476b2c736861646f775f31302c746578745.jpeg

总结:在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。

相信看到这里,大家已经明白为何答案为10 10 12 13 。

上一篇 下一篇

猜你喜欢

热点阅读