Демистификация React setState

внешний интерфейс JavaScript React.js
Перенесено из сообщества IMWeb, автор: Huang Qiong,Оригинальная ссылка

предисловие

Любой, кто изучал реакцию, знает, что setState — очень важный метод в реакции. С его помощью можно обновить состояние наших данных. Эта статья начинается сПростой в использованииприбытьУглубитесь во внутренности setState, раскройте для вас тайну setState во всех направлениях~

Меры предосторожности при использовании setState

setState(updater, callback)Этот метод используется, чтобы сообщить компоненту реакции, что данные были обновлены и, возможно, потребуется повторная визуализация. Он асинхронный, и react обычно собирает партию компонентов, которые необходимо обновить, а затем обновляет их за один раз, чтобы убедиться, чтопроизводительность рендеринга, так что это зарыло для нас дыру:

который используетsetStateПосле смены состояния сразу пройтиthis.stateПолучение последнего статуса часто недоступно.

пункт один

Итак, первая точка использования: если вам нужно вести бизнес на основе последнего состояния, вы можетеcomponentDidUpdateилиsetStateполученный из функции обратного вызова. (Примечание: первый метод официально рекомендуется)

// setState回调函数
changeTitle: function (event) {
  this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
  // Call API with the updated value
}

пункт два

Предположим, есть требование, которое нужно накопить дважды в onClick, как показано ниже.

  onClick = () => {
    this.setState({ index: this.state.index + 1 });
    this.setState({ index: this.state.index + 1 });
  }

В глазах реагирующих этот метод в конечном итоге станет

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

Поскольку более поздние данные перезапишут предыдущие изменения, они добавляются только один раз, поэтому, если следующее состояние зависит от предыдущего состояния, рекомендуется передать функцию в setState.

onClick = () => {
    this.setState((prevState, props) => {
      return {quantity: prevState.quantity + 1};
    });
    this.setState((prevState, props) => {
      return {quantity: prevState.quantity + 1};
    });
}

Выше приведены две меры предосторожности при использовании setState.Далее давайте рассмотрим процесс обновления компонентов после вызова setState.Ниже представлена ​​простая блок-схема.

Ниже приведен пошаговый анализ процесса на схеме.

1. установить состояние

ReactBaseClassses.js

ReactComponent.prototype.setState = function (partialState, callback) {
  //  将setState事务放进队列中
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

Здесь partialState может передавать объект или функцию, которые будут генерировать новое состояние сObject.assgine()способ слиться со старым состоянием.

2. enqueueSetState

  enqueueSetState: function (publicInstance, partialState) {
     // 获取当前组件的instance
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');

     // 将要更新的state放入一个数组里
     var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);

     //  将要更新的component instance也放在一个队列里
    enqueueUpdate(internalInstance);
  }

Этот код показывает, что enqueueSetState делает две вещи: 1. Помещает новое состояние в массив 2. Использует enqueueUpdate для обработки обновляемого объекта экземпляра.

Три, поставить в очередьОбновление

ReactUpdates.js

function enqueueUpdate(component) {
  // 如果没有处于批量创建/更新组件的阶段,则处理update state事务
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  // 如果正处于批量创建/更新组件的过程,将当前的组件放在dirtyComponents数组中
  dirtyComponents.push(component);
}

Как видно из этого кода, если вы сейчас находитесь в процессе создания/обновления компонента, вы не будете обновлять компонент немедленно, а сначала поместите текущий компонент в dirtyComponent, поэтому не каждый setState будет обновлять компонент~.

Этот код объясняет то, что мы часто слышим:setState — это асинхронный процесс, который собирает пакет компонентов, которые необходимо обновить, и обновляет их вместе..

А что такое пакетная стратегия?

В-четвертых, пакетная стратегия

ReactDefaultBatchingStrategy.js

var ReactDefaultBatchingStrategy = {
  // 用于标记当前是否出于批量更新
  isBatchingUpdates: false,
  // 当调用这个方法时,正式开始批量更新
  batchedUpdates: function (callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // 如果当前事务正在更新过程在中,则调用callback,既enqueueUpdate
    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
    // 否则执行更新事务
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  }
};

Обратите внимание на два момента: 1. Если текущая транзакция находится в процессе обновления, используйтеenqueueUpdateпоместить текущий компонент вdirtyComponentвнутри. 2. Если он в данный момент не находится в процессе обновления, выполните транзакцию обновления.

Пять, транзакция

/**
 * <pre>
 *                       wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+
 * </pre>
 */

Кратко объясните объект транзакции, который предоставляет метод выполнения для выполнения любого метода. Перед выполнением любого метода необходимо выполнить метод инициализации всех оболочек. После выполнения необходимо выполнить метод закрытия всех оболочек. Простой.

В ReactDefaultBatchingStrategy.js есть две обертки для tranction.FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }
};

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

Как видите, две оберткиinitializeНичего не было сделано, но после выполнения обратного вызова функция RESET_BATCHED_UPDATES должна установить для isBatchingUpdates значение false, а функция FLUSH_BATCHED_UPDATES — выполнить flushBatchedUpdates, после чего все грязные компоненты будут зациклены внутри, а updateComponent будет вызываться для выполнения всех методы жизненного цикла, componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate, наконец, реализуют обновление компонента.

Выше приведен процесс реализации setState. Наконец, блок-схема используется для подведения итогов~

Справочная документация:

  1. zhuanlan.zhihu.com/p/25882602