由实际问题探究setState的执行机制

一.几个开发中经常会遇到的问题

以下几个问题是我们在实际开发中经常会遇到的场景,下面用几个简单的示例代码来还原一下。

1.setState是同步还是异步的,为什么有的时候不能立即拿到更新结果而有的时候可以?

1.1 钩子函数和React合成事件中的 
  1.  componentDidMount() {

  2.    console.log('parent componentDidMount');

  3.  }

  4.  render() {

  5.    return (

  6.      <div>

  7.        <SetState2></SetState2>

  8.        <SetState></SetState>

  9.      </div>

  10.    );

  11.  }

组件内部放入同样的代码,并在 componentDidMount中放入一段同步延时代码,打印延时时间:

setState
不会立即更新
  • 2.所有组件使用的是同一套更新机制,当所有组件 didmount,然后执行更新

  • 3.更新时会把每个组件的更新合并,每个组件只会触发一次更新的生命周期。

1.2 异步函数和原生事件中的 setTimeout中调用
  1.  componentDidMount() {

  2.    setTimeout(() => {

  3.      console.log('调用setState');

  4.      this.setState({

  5.        index: this.state.index + 1

  6.      })

  7.      console.log('state', this.state.index);

  8.      console.log('调用setState');

  9.      this.setState({

  10.        index: this.state.index + 1

  11.      })

  12.      console.log('state', this.state.index);

  13.    }, 0);

  14.  }

执行结果:

说明:

  • 1.在父组件 setState同步更新

2.为什么有时连续两次 
  1.  componentDidMount() {

  2.    this.setState({ index: this.state.index + 1 }, () => {

  3.      console.log(this.state.index);

  4.    })

  5.    this.setState({ index: this.state.index + 1 }, () => {

  6.      console.log(this.state.index);

  7.    })

  8.  }

1
1
setstate
会被合并成一次
  • 2.使用函数传递 githubpartialState: _pendingStateQueue:当前组件等待执行更新的 isBatchingUpdates:react用于标识当前是否处于批量更新状态,所有组件公用

  • transcation:react的事务机制,在被事务调用的方法外包装n个 waper.init、被调用方法、 FLUSH_BATCHED_UPDATES:用于执行更新的 close方法

2.执行过程

对照上面流程图的文字说明,大概可分为以下几步:

  • 1.将setState传入的 waper方法,遍历待更新组件队列依次执行更新。

  • 5.执行生命周期 state进行合并,获得最终要更新的state对象,并将队列置为空。

  • 7.执行生命周期 componentWillUpdate

  • 9.执行真正的更新, componentDidUpdate

三.总结

1.钩子函数和合成事件中:

react仍然处于他的更新机制中,这时 setState,都会不会执行更新,而是将要更新的 _pendingStateQueue,将要更新的组件存入 didmount后会将 setState

2.异步函数和原生事件中

由执行机制看, setState时,如果 isBranchUpdate被设置为false,根据上面的流程,这时再调用 partialState合并机制

我们看下流程中 state暂存队列的,最后返回一个合并后的

  1.  _processPendingState: function (props, context) {

  2.    var inst = this._instance;

  3.    var queue = this._pendingStateQueue;

  4.    var replace = this._pendingReplaceState;

  5.    this._pendingReplaceState = false;

  6.    this._pendingStateQueue = null;

  7.    if (!queue) {

  8.      return inst.state;

  9.    }

  10.    if (replace && queue.length === 1) {

  11.      return queue[0];

  12.    }

  13.    var nextState = _assign({}, replace ? queue[0] : inst.state);

  14.    for (var i = replace ? 1 : 0; i < queue.length; i++) {

  15.      var partial = queue[i];

  16.      _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);

  17.    }

  18.    return nextState;

  19.  },

我们只需要关注下面这段代码:

Object.assign(
  nextState,
  {index: state.index+ 1},
  {index: state.index+ 1}
)

如果传入的是函数,函数的参数preState是前一次合并后的结果,所以计算结果是准确的。

4. setstate

在componentDidMount()中,你 可以立即调用setState()。它将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了在此情况下即使render()将会调用两次,用户也不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。在大多数情况下,你可以 在constructor()中使用赋值初始状态来代替。然而,有些情况下必须这样,比如像模态框和工具提示框。这时,你需要先测量这些DOM节点,才能渲染依赖尺寸或者位置的某些东西。

以上是官方文档的说明,不推荐直接在 setState,由上面的分析: setState,就会在未来再进行一次 componentDidMount我们可以调用接口,再回调中去修改 componentDidMountcomponentWillUpdate setState

由上面的流程图很容易发现,在它们里面调用 setState时使用函数传递 state

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值