返回

React setState 全解析:深入源码挖掘执行机制,揭秘同步异步表现及对象参数设置问题

前端

深入源码理解 setState 执行机制

  _pendingState: null,
  _pendingCallbacks: null,

setState 方法其实是一个非阻塞操作,不会阻塞页面的渲染。React 会先将更新信息存储在_pendingState和_pendingCallbacks中,然后在下一帧才会触发真正的组件重新渲染。

  if (updateBatching === null) {
    updateBatching = 0;
    // Transition to start a batch.
    renderPhaseUpdates = [];
    ReactCurrentBatchConfig = batchConfig;
  }

当触发setState时,React 会检查一个全局变量updateBatching是否为null。如果为null,则创建一个新的批处理,并将setState的信息存储在renderPhaseUpdates中。

  // Previous state, if we're not currently
  // batching an update
  if (updateBatching > 0) {
    shouldComponentUpdate = false;
    prevState = updater.isReplace
      ? emptyObject
      : combinedState;
    newSimpleState = booleanKey ? newSimpleState[0] : newSimpleState;
    isStateObj = typeof newSimpleState === 'object';
  }

如果当前正在批处理更新,则shouldComponentUpdate设置为false,prevState设置为当前的状态,newSimpleState设置为新的状态。

  if (
    typeof updater === 'function'
  ) {
    updater = updater.call(
      currentState,
      prevState,
      props
    );

如果updater是一个函数,则调用该函数,并以当前状态、prevState和props作为参数。

  nextUpdater = null;
  if (nextUpdater) {
    // Determine what the next state should be, and recompute
    // the nextPropSet if propDeps changed. We might cycle through
    // several sets of updates while processing.
    var nextState = nextUpdater.call(
      currentState,
      combinedState,
      nextPropSet
    );

如果nextUpdater不为空,则调用nextUpdater,并以当前状态、combinedState和nextPropSet作为参数。

  currentState = combinedState;
  prevProps = currentProps;
  currentProps = nextPropSet;
  if (currentContext === null) {
    currentContext = emptyContextObject;
  }

将currentState设置为combinedState,prevProps设置为currentProps,currentProps设置为nextPropSet,currentContext设置为emptyContextObject。

  updateBatching = null;
  ReactCurrentBatchConfig = null;
  renderPhaseUpdates.forEach(beginPhase);

将updateBatching和ReactCurrentBatchConfig设置为null,并调用beginPhase函数,对renderPhaseUpdates中的每个更新进行处理。

同步/异步执行的背后原因

setState 的同步/异步执行取决于当前是否处于事件处理过程中。如果在事件处理过程中调用 setState,则该更新是异步的;否则,该更新是同步的。

这是因为 React 在事件处理过程中不会立即更新状态,而是将更新信息存储在_pendingState和_pendingCallbacks中,然后在下一帧才会触发真正的组件重新渲染。这样做是为了避免在事件处理过程中对 DOM 进行不必要的更新,从而提高性能。

对象参数设置仅生效最后一次的原因

当使用对象作为 setState 的参数时,只有最后一次设置的对象参数才会生效。这是因为 React 在更新状态时,会将新的状态与之前的状态进行合并,如果新的状态也是一个对象,则会覆盖之前的状态,从而导致只有最后一次设置的对象参数生效。

为了解决这个问题,可以将对象参数中的属性逐个传入 setState,这样就可以确保每个属性都能正确更新。

this.setState({
  name: 'Alice',
  age: 20,
});

结语

setState 是 React 中一项非常重要的工具,它允许组件更新自己的状态,从而触发重新渲染。通过深入了解 setState 的执行机制,我们能够更好地理解 React 的工作原理,并避免在使用 setState 时遇到问题。