关于javascript:React-中setState更新state何时同步何时异步

39次阅读

共计 2288 个字符,预计需要花费 6 分钟才能阅读完成。

先说论断

  • React 管制的事件处理程序,以及生命周期内调用 setState 是异步更新state
  • React管制之外的事件中调用 setState 是同步更新 state,比方原生 js 绑定事件、setTimeout/setInrerval 等。

setState的“异步”并不是说外部由异步代码实现,自身的执行过程和代码都是同步的。

之所以会有一种异步办法的表现形式,归根结底还是因为 React 框架自身的性能机制所导致的。因为每次调用 setState 都会触发更新,异步操作是为了进步性能,将多个状态合并一起更新,缩小 re-render 调用。

咱们来看一些例子:

React 封装事件

state = {number: 1}

componentDidMount() {this.setState({ number: 3});
  console.log(this.state.number); // 1
}

由此可见该事件处理程序中的 setState 是异步更新 state 的。

setTimeout

state = {number: 1}

componentDidMount() {setTimeout(() => {this.setState({ number: 3});
  }, 0);
  console.log(this.state.number); // 3
}

下面咱们讲到,setState自身并不是一个异步办法,之所以会变现出一种异步的模式,是因为 React 框架自身的一种性能优化机制。那么基于这一点,如果咱们能绕过 React 的机制,就能够令 setState 以同步的模式体现。

原生事件

state = {number: 1}

componentDidMount() {document.body.addEventListener('click', this.resetState, false);
}

resetState() {this.setState({ number: 3});
  console.log(this.state.number); // 3
}

同样的,原生事件也能够绕过 React 的性能优化机制,达到同步更新的体现。

React 是如何管制异步和同步的?

ReactsetState函数实现中,会依据一个变量 isBatchingUpdates 判断是否间接更新this.state,还是放入队列中延时更新。

isBatchingUpdates 默认是 false,标识setState 是同步更新 this.state。然而有一个函数batchedUpdates 会把 isBatchingUpdates 批改为 true,而当React 在调用事件处理函数之前就会先调用这个函数将 isBatchingUpdates 批改为 true。这样由React 管制的事件处理过程 setState 就不会同步更新this.state

function enqueueUpdate(component){
  //injected 注入的
  ensureInjected();
  // 如果不处于批量更新模式
  if(!batchingStrategy.isBatchingUpdates){batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  // 如果处于批量更新模式
  dirtyComponents.push(component);
}

事实上 setState 外部的执行过程是很简单的,大抵过程包含更新 state,创立新的VNode,再通过diff 算法比照差别,决定渲染哪一部分以及怎么渲染,最终造成最新的 UI。这一过程蕴含组件的四个生命周期函数:

  • shouComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

如果是子组件并且依赖父组件,还会执行一个钩子函数componentWillReceiveProps

如果 setState 是同步更新的,每次更新这个过程都要残缺执行一次,无疑会造成性能问题。事实上这些生命周期为纯函数,对性能还好,然而 diff 比拟、更新 DOM 总耗费工夫和性能吧。

在“异步”中如果对同一个值进行屡次setStatesetState 的批量更新策略会对其进行笼罩,取最初一次的执行。

state = {number: 1}

handleClick() {
  const number = this.state.number;

  this.setState({number: number + 1});
  this.setState({number: number + 1});
  this.setState({number: number + 1});
}

最终的后果只加了 1。

如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。

hanldeClick() {this.setState({ name: 'Clearlove'});
  this.setState({age: 18});
}

hanldeClick 处理程序中调用了两次 setState,然而render 只执行了一次。因为 React 会将多个 this.setState 产生的批改放在一个队列里进行批延时解决。

如何获取“异步”更新后的数据?

setState提供了一个回调函数供开发者应用,在回调函数中,咱们能够实时的获取到更新之后的数据。还是以方才的例子做示范:

state = {number: 1}

componentDidMount() {this.setState({ number: 3}, () => {console.log(this.state.number); // 3
  });
}

正文完
 0