AlienGao's blog AlienGao's blog
首页
  • JavaScript
  • Antd组件
  • 学习笔记

    • 《ES6 教程》笔记
  • 贪心
  • 广度优先/深度优先
  • 位运算
  • 技术文档
  • GitHub技巧
面试
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

AlienGao

首页
  • JavaScript
  • Antd组件
  • 学习笔记

    • 《ES6 教程》笔记
  • 贪心
  • 广度优先/深度优先
  • 位运算
  • 技术文档
  • GitHub技巧
面试
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • React和Vue列表中Key的作用
  • 谈一谈 nextTick 的原理
  • 为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作?
  • Vue 的父组件和子组件生命周期钩子执行顺序是什么
  • React 中 setState 什么时候是同步的,什么时候是异步的?
  • Vue查漏补缺
  • 框架
AlienGao
2021-08-12

React 中 setState 什么时候是同步的,什么时候是异步的?

React中正常 state 更新、UI 交互,都离不开用户的事件,比如点击事件,表单输入等,React 是采用事件合成的形式,每一个事件都是由 React 事件系统统一调度的,那么 State 批量更新正是和事件系统息息相关的。 以下为一个点击事件的例子:

export default class index extends React.Component{
    state = { number:0 }
    handleClick= () => {
          this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback1', this.state.number)  })
          console.log(this.state.number)
          this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback2', this.state.number)  })
          console.log(this.state.number)
          this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback3', this.state.number)  })
          console.log(this.state.number)
    }
    render(){
        return <div>
            { this.state.number }
            <button onClick={ this.handleClick }  >number++</button>
        </div>
    }
} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

输出结果为:0 0 0 callback1 1 callback2 1 callback3 1 其中的执行过程如下: React事件系统流程 通过开启批量更新开关来处理点击事件,在批量更新条件下合并state结果。

但是异步操作会破坏React的批量更新规则。比如下面的例子:

setTimeout(()=>{
    this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback1', this.state.number)  })
    console.log(this.state.number)
    this.setState({ number:this.state.number + 1 },()=>{    console.log( 'callback2', this.state.number)  })
    console.log(this.state.number)
    this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback3', this.state.number)  })
    console.log(this.state.number)
})
1
2
3
4
5
6
7
8

输出结果为:callback1 1 1 callback2 2 2 callback3 3 3 此时事件系统的执行过程变为:

异步操作中事件处理流程

所以批量更新规则被打破。

那么,如何在如上异步环境下,继续开启批量更新模式呢?

React-Dom 中提供了批量更新方法 unstable_batchedUpdates,可以去手动批量更新,可以将上述 setTimeout 里面的内容做如下修改:

import ReactDOM from 'react-dom'
const { unstable_batchedUpdates } = ReactDOM
1
2
setTimeout(()=>{
    unstable_batchedUpdates(()=>{
        this.setState({ number:this.state.number + 1 })
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1})
        console.log(this.state.number)
        this.setState({ number:this.state.number + 1 })
        console.log(this.state.number) 
    })
})

1
2
3
4
5
6
7
8
9
10
11

输出结果: 0 , 0 , 0 , callback1 1 , callback2 1 ,callback3 1

React-dom 提供了 flushSync ,flushSync 可以将回调函数中的更新任务,放在一个较高的优先级中。React 设定了很多不同优先级的更新任务。如果一次更新任务在 flushSync 回调函数内部,那么将获得一个较高优先级的更新。

handerClick=()=>{
    setTimeout(()=>{
        this.setState({ number: 1  })
    })
    this.setState({ number: 2  })
    ReactDOM.flushSync(()=>{
        this.setState({ number: 3  })
    })
    this.setState({ number: 4  })
}
render(){
   console.log(this.state.number)
   return ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在这个例子中通过ReactDOM的flushSync方法提高了回调函数函数的优先级,所以最先执行number = 3。然后进行批量更新,合并了number = 2 和number = 4,结果输出4。最后执行异步操作,返回1。

编辑 (opens new window)
#框架
上次更新: 2021/08/17, 00:26:05
Vue 的父组件和子组件生命周期钩子执行顺序是什么
Vue查漏补缺

← Vue 的父组件和子组件生命周期钩子执行顺序是什么 Vue查漏补缺→

最近更新
01
Promise
08-18
02
Vue查漏补缺
08-16
03
筛选组件
08-14
更多文章>
Theme by Vdoing | Copyright © 2021-2021 AlienGao | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×