react之setState

1、异步更新机制

我们知道,this.state是只读的,更新状态不能直接修改,而是通过this.setState方法。这是为什么呢?this.state只是一个对象,我们修改它的值是没有意义的。仔细想一想,我们之所以要修改state,无非是为了改变页面的渲染状态;所以React设计setState方法就是为了重新渲染页面。
我们可以在setState之后打印一下this.state的值,会发现它并没有改变,还是之前的值。如果我们需要在短时间内多次setState,并且每次setState的值跟之前的状态有关,我们就需要使用函数作为setState的参数了,这个函数参数接收两个参数(当前的state和当前的props)。举个例子:

// 最终产生的结果是this.state.value只增加了1
function test1() {
    this.setState({ value: this.state.value + 1 });
    this.setState({ value: this.state.value + 1 });
    this.setState({ value: this.state.value + 1 });
}

多次setState会合并

前面我们了解到setState并不会立即改变state的值,而是将其放到一个任务队列里,最终将多个setState合并,一次性更新页面。所以我们可以在代码里多次调用setState,每次只需要关注当前修改的字段即可。
另外,需要注意的是,setState触发页面重新渲染需要经过以下生命周期:

shouldComponentUpdate(){}
componentWillUpdate(){}
render(){
// 更新state值
}
componentDidUpdate(){}
// 换种写法,结果就如我们所意了
function test2() {
    this.setState((state, props) => ({ value: state.value + 1 }));
    this.setState((state, props) => ({ value: state.value + 1 }));
    this.setState((state, props) => ({ value: state.value + 1 }));
}

要注意的是,在以上的setState中,this.state并没有被改变,依然,要等到render函数被重新执行时(或者shouldComponentUpdate函数返回false之后)才被改变。

因为使用函数式setState,React会保证每次调用函数时,state都已经合并了之前的状态修改结果。
setState还有第二个参数callback,所以下面这种写法也是可以的:

this.setState({ value: this.state.value + 1 }, (val) => {
  this.setState({ value: this.state.value + 1 }, () => {
    this.setState({ value: this.state.vavlue + 1 });
  });
});

2、同步更新机制

在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state。

 componentDidMount() {
     // 同步跟新
    document.querySelector('#btn-raw').addEventListener('click', this.onClick);
 }
 onClickLater() {
    // 同步更新
    setTimeout(() => {
      this.onClick();
    });
  }
  onClick() {
  // 异步
    this.setState({count: this.state.count + 1});
    console.log('# this.state', this.state);
  }
  render() {
    console.log('#enter render');
    return (
      <div>
        <div>{this.state.count}
          <button onClick={this.onClick}>Increment</button>
          <button id="btn-raw">Increment Raw</button>
          <button onClick={this.onClickLater}>Increment Later</button>
        </div>
      </div>
    )

  }

同步更新this.state的话,每一次调用setState都会引发同步的更新过程,这会更新过程很频繁,也就会导致性能问题。

3、异步更新设计原因

  • 保证内部状态一致性
  • 性能优化
  • 更多的可能行

详情参见:https://juejin.im/post/5a6f440a51882573336652af

在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。

4、总结

  • setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout中都是同步的。
  • setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。

参考文章:https://juejin.im/post/5a393cf9f265da432c23fb3b

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值