理想是火,点燃熄灭的灯。
上上篇博客我讲了setState() 的批处理合并,而setState()是异步的还是同步的,和setState() 的批处理有很大的关系,推荐先看完上上篇博客再来看这篇,会清晰很多
地址如下:http://biaoblog.cn/#/bookInfo/1625016158478
setState()批处理
先看异步的情况:
import React, { Component } from 'react'; class com2 extends Component { state = { num: 0 } add = () => { this.setState({ num: this.state.num + 1 }) console.log(this.state.num) this.setState({ num: this.state.num + 2 }) console.log(this.state.num) this.setState({ num: this.state.num + 3 }) console.log(this.state.num) } render() { return ( <div> <button onClick={this.add}>按钮{this.state.num}</button> </div> ); } } export default com2;
结果如下:
之前说过,react内部为了优化setState()的批处理,会对setState()进行合并,并且对相同属性的设置只保留最后一次的设置,所按钮上3是可以理解的,
但是打印出三次0,那又是为啥?
在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到一个updateQueue中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state,而是异步的。
总结:
所以说setstate本身是同步的,一旦走了react内部的合并逻辑,放入了updateQueue队列中就变成异步了,而代码中的函数是react控制的,内部会走合并逻辑,所以这里的setState 不但是合并的也是异步的,所以打印出三个0
上面说了,setState是异步的原因是因为走了react内部的合并逻辑,那只要能绕过react内部的合并逻辑,不让它进入到updateQueue中不就变成同步了吗?因为setState()本身就是同步的
利用setTimeout绕过react内部的合并逻辑
import React, { Component } from 'react'; class com2 extends Component { state = { num: 0 } add = () => { //利用setTimeout绕过react的控制,不让setState()走合并逻辑 setTimeout(() => { this.setState({ num: this.state.num + 1 }) console.log(this.state.num) this.setState({ num: this.state.num + 2 }) console.log(this.state.num) this.setState({ num: this.state.num + 3 }) console.log(this.state.num) }); } render() { return ( <div> <button onClick={this.add}>按钮{this.state.num}</button> </div> ); } } export default com2;
结果如下:
总结:
异步的情况:
由React控制的事件处理函数,以及生命周期函数调用setState时表现为异步 。大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等(合成事件中),这些事件处理函数中的setState都是异步处理的。
同步的情况:
React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval,ajax,promise.then内等 React 无法掌控的 APIs情况下,setState是同步更新state的,
值得一提的是
setState()可以接收一个对象外,还可以接收一个函数:
传递对象
批处理,对相同变量进行的多次处理会合并为一个,并以最后一次的处理结果为准
传递函数
链式调用,React 会把我们更新 state 的函数加入到一个队列里面,然后,按照函数的顺序依次调用。同时,为每个函数传入 state 的前一个状态,这样,就能更合理的来更新我们的 state 了,该函数有两个参数
prevState
props
这和我们前面理解的setState()异步执行不冲突
可以理解为:
本来我想拿到上一次setState() 执行完后的结果,需要使用一些特殊的方式,绕开合并逻辑,让setState() 保持本身的同步执行特性,代码如下:
import React, { Component } from 'react'; class com2 extends Component { state = { count: 0 } add = () => { setTimeout(()=>{ this.setState({ count: this.state.count + 1 }) console.log(this.state.count) this.setState({ count: this.state.count + 1 }) console.log(this.state.count) this.setState({ count: this.state.count + 1 }) console.log(this.state.count) }) } render() { return ( <div> <button onClick={this.add}>按钮{this.state.count}</button> </div> ); } } export default com2;
点击后结果:
而现在,在异步执行的情况下,我还想拿到上一次setState执行完后的结果,react给我们提供了方式,就是在setState()里面传入一个函数,代码如下:
import React, { Component } from 'react'; class com2 extends Component { state = { count: 0 } add = () => { this.setState((state) => { // 重要:在更新的时候读取 `state`,而不是 `this.state`。 return { count: state.count + 1 } }); console.log(this.state.count) this.setState((state) => { // 重要:在更新的时候读取 `state`,而不是 `this.state`。 return { count: state.count + 1 } }); console.log(this.state.count) this.setState((state) => { // 重要:在更新的时候读取 `state`,而不是 `this.state`。 return { count: state.count + 1 } }); console.log(this.state.count) } render() { return ( <div> <button onClick={this.add}>按钮{this.state.count}</button> </div> ); } } export default com2;
结果如下:
在setState中传一个函数,能拿到上次setState执行完后的结果,但是不妨碍是异步更新的,可以看到打印的是0,这是react给我们提供的方便之处
转载:https://blog.csdn.net/fesfsefgs/article/details/108036605
作者: Bill 本文地址: http://biaoblog.cn/info?id=1625017790526
版权声明: 本文为原创文章,版权归 biaoblog 个人博客 所有,欢迎分享本文,转载请保留出处,谢谢!
上一篇:ViewUi的render
下一篇:setState()批处理