setState 总结

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

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 。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 12,696评论 1 33
  • 写业务代码的时候 需要经常用到setState, 前几天review代码的时候, 又想了一下这个API, 发现对它...
    world_7735阅读 5,546评论 0 3
  • 个人笔记, 转载请注明转载自 szhshp的第三边境研究所 Refs and the DOM In the t...
    szhielelp阅读 5,345评论 0 1
  • 40、React 什么是React?React 是一个用于构建用户界面的框架(采用的是MVC模式):集中处理VIE...
    萌妹撒阅读 4,704评论 0 1
  • 深入浅出setState 众所周知,react.js是将数据储存在state中,通过this.state来访问st...
    老夫QAQ阅读 4,980评论 0 6

友情链接更多精彩内容