React 篇之浅谈 setState is异步OR同步

React 篇之浅谈 setState is异步OR同步

  react 篇主要是记录笔者之前在使用 React 进行开发时遇到的问题和坑, 趁还没有毕业 一 一 查阅文档资料总结归纳, 和大家一起分享, 以防重蹈覆辙.

  这篇文章主要总结的是关于利用 setState 更改组件状态时遇到的一些坑, 希望会的小伙伴可以当做复习巩固,不会的可以当做学习.

setState 究竟是异步的还是同步的,为何有的时候是同步,有的时候异步呢?

setState 异步更新

案例

class Example extends React.Component{
    constructor(){
    super(...arguments)
        this.state = {
            count: 0
        };
    }
    componentDidMount(){
        //第一次更新状态
        this.setState({count: this.state.count + 1});
        console.log(this.state.count)
        //第二次更新状态
        this.setState({count: this.state.count + 1});
        console.log(this.state.count)
        setTimeout(() => console.log(this.state.count),0)
    }
}

运行结果依次是:

0 0 1

这就有点纳闷了, 打印出来的不应该是 1 2 2 吗?

其实这里的状态更新是'异步'的, setState 是通过一个队列机制来实现state更新的, 当执行setState()时, 就会将需要更新的 state 合并 (浅合并!!) 后在放入状态队列中, 而不是立即更新 state, react 中的状态队列机制可以起到高效批量更新state

由此可以知道 React 的 setState 是通过状态队列机制实现的, 以此避免了重复的更新的 state

另外在上文提到的setState会将需要更新的state合并,这是怎么回事呢?

setState(nextState[,callback])

React 官方文档对 setState 有明确的介绍到:

 当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state

举个例子:

this.setState({count: state.count + 1})
this.setState({count: state.count + 2})
this.setState({count: state.count + 3})

//结果的state.count 值为 0

当你同时对同一个或多个几个状态进行更新时,就等同于

this.setState(Object.assign(state,
    {count: state.count + 1},
    {count: state.count + 2},
    {count: state.count + 3}
))

那么, 如果在开发中, 迫不得已需要对一个状态多次更新,但又要保证这个状态是可靠的,没方法了吗?

别急, 你可以想到的官方也有想到过, 要解决这个问题, 可以让 setState() 接收一个函数而不是一个对象. 这个函数用上一个 state 作为第一个参数, 将此次更新被应用时的 props 做为第二个参数.

使用方法:

//假设 this.props = {addVal: 1}
this.setState((preState,props) => ({
    count: preState.count + props.addVal
}))

此时的setState()有点像数组的renduce(累加器)

//假设 this.props = {addVal: 1}
Array(upDateCount)
    .fill(this.props)
    .reduce((preState,props) => ({
        count: preState + props.addVal
    }),{count: 0})

    // 最终 {count: 3}

setState 同步更新

案例

class Example2 extends React.Component{
    constructor(){
        super(...arguments)
        this.state = {
            count: 0
        };
    }

    componentDidMount(){
        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)
        },0)
    }
}

运行结果依次是:

1 2

既然说 setState 是异步的, 那么结果应该是0 0,为何可以正常捕获到状态更新完的值呢, 然而setState不尽然在任何地方都是异步的, setTimeout 中调用以及原生事件中调用的话, 是可以立马获取到最新的 state 的.

其实在 React 中, 如果是由 React 引发的事件处理即合成事件(比如通过onClick引发的事件处理) 或者钩子函数(如生命周期函数等绑定事件函数), 调用setState不会同步更新this.state, 除此之外的setState调用会同步执行this.state, 所谓'除此之外',指的是绕过 React 通过addEventListener(原生事件)直接添加的事件处理函数, 还有通过setTimeout/setInterval(定时器)产生的异步调用。

那么 React 究竟何时处于异步,何时同步呢?

在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdate 来判断是直接同步更新 this.state 还是放到队列中异步更新 。React 使用了事务的机制,React 的每个生命周期和合成事件都处在一个大的事务当中。在事务的前置钩子中调用 batchedUpdates 方法修改 isBatchingUpdates 变量为 true,在后置钩子中将变量置为 false。原生绑定事件和 setTimeout 异步的函数没有进入到 React 的事务当中,或者当他们执行时,刚刚的事务已近结束了,后置钩子触发了,所以此时的 setState 会直接进入非批量更新模式,表现在我们看来成为了同步 SetState。

关于 React 中的setState何时属于异步,何时异步点到为止, 笔者目前能力有限(学不过来),只能在自己的理解范围内讨论, 只能在今后的码路上继续前进,进一步深入了解

你可能感兴趣的:(React 篇之浅谈 setState is异步OR同步)