美文网首页
React 篇之浅谈 setState is异步OR同步

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

作者: 进击的切图仔 | 来源:发表于2019-11-01 02:40 被阅读0次

    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同步

          本文链接:https://www.haomeiwen.com/subject/miixbctx.html