análisis de código fuente setstate

fuente setstate

método de entrada

// setState方法入口如下:
ReactComponent.prototype.setState = function (partialState, callback) {
  // 将setState事务放入队列中
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }};

replaceState:

replaceState: function (newState, callback) {
  this.updater.enqueueReplaceState(this, newState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'replaceState');
  }},
  // replaceState中取名为newState,有完全替换的含义。同样也是以队列的形式来管理的。

enqueueSetState

enqueueSetState: function (publicInstance, partialState) {
    // 先获取ReactComponent组件对象
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');

    if (!internalInstance) {
      return;
    }

    // 如果_pendingStateQueue为空,则创建它。可以发现队列是数组形式实现的
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);

    // 将要更新的ReactComponent放入数组中
    enqueueUpdate(internalInstance);}

En el que getInternalInstanceReadyForUpdatela fuente como

function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
  // 从map取出ReactComponent组件,还记得mountComponent时把ReactElement作为key,将ReactComponent存入了map中了吧,ReactComponent是React组件的核心,包含各种状态,数据和操作方法。而ReactElement则仅仅是一个数据类。
  var internalInstance = ReactInstanceMap.get(publicInstance);
  if (!internalInstance) {
    return null;
  }

 return internalInstance;}

código fuente enqueueUpdate como sigue:

function enqueueUpdate(component) {
  ensureInjected();

  // 如果不是正处于创建或更新组件阶段,则处理update事务
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

  // 如果正在创建或更新组件,则暂且先不处理update,只是将组件放在dirtyComponents数组中
  dirtyComponents.push(component);}

batchedUpdates

batchedUpdates: function (callback, a, b, c, d, e) {
  var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
  // 批处理最开始时,将isBatchingUpdates设为true,表明正在更新
  ReactDefaultBatchingStrategy.isBatchingUpdates = true;

  // The code is written this way to avoid extra allocations
  if (alreadyBatchingUpdates) {
    callback(a, b, c, d, e);
  } else {
    // 以事务的方式处理updates,后面详细分析transaction
    transaction.perform(callback, null, a, b, c, d, e);
  }}
var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    // 事务批更新处理结束时,将isBatchingUpdates设为了false
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }};var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

enqueueUpdateReaccionar contiene 避免重复renderla lógica.

mountComponentY un updateComponentmétodo de ejecución en un principio, será llamado a batchedUpdatesrealizar 批处理la actualización, será esta vez isBatchingUpdatesestablecido true, es decir, el estado ahora está marcado como 正处于更新阶段una.

Después Reaccionar transacción manera el procesamiento de actualización de componentes, después de la transacción llama wrapper.close ().

Y TRANSACTION_WRAPPERSse incluye RESET_BATCHED_UPDATESen esta wrapper, con el tiempo se llame RESET_BATCHED_UPDATES.close(), que eventualmente isBatchingUpdatesestablecido false.

Por lo tanto getInitialState, componentWillMount, render, componentWillUpdateen la setStatevoluntad no causar updateComponent.

Sin embargo, en componentDidMounty componentDidUpdatede la voluntad.

negocios

Transacción wrapperencapsulado.

clipboard.png

A wrapperun par de contener initializey closemétodos. Por ejemplo RESET_BATCHED_UPDATES:

var RESET_BATCHED_UPDATES = {
  // 初始化调用
  initialize: emptyFunction,
  // 事务执行完成,close时调用
  close: function () {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }};

transcation está empaquetado en una envoltura, tales como:

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

transactionA través de transaction.perform(callback, args…)método para entrar, primero llame buena registrada wrapperen el initializemétodo, y luego ejecutar performmétodos callback, y finalmente la ejecución closedel método.

El siguiente análisis transaction.perform (devolución de llamada, args ...)

  perform: function (method, scope, a, b, c, d, e, f) {
    var errorThrown;
    var ret;
    try {
      this._isInTransaction = true;
      errorThrown = true;
      // 先运行所有wrapper中的initialize方法
      this.initializeAll(0);

      // 再执行perform方法传入的callback
      ret = method.call(scope, a, b, c, d, e, f);
      errorThrown = false;
    } finally {
      try {
        if (errorThrown) {
          // 最后运行wrapper中的close方法
          try {
            this.closeAll(0);
          } catch (err) {}
        } else {
          // 最后运行wrapper中的close方法
          this.closeAll(0);
        }
      } finally {
        this._isInTransaction = false;
      }
    }
    return ret;
  },

  initializeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    // 遍历所有注册的wrapper
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      try {
        this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
        // 调用wrapper的initialize方法
        this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
      } finally {
        if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
          try {
            this.initializeAll(i + 1);
          } catch (err) {}
        }
      }
    }
  },

  closeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    // 遍历所有wrapper
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var initData = this.wrapperInitData[i];
      var errorThrown;
      try {
        errorThrown = true;
        if (initData !== Transaction.OBSERVED_ERROR && wrapper.close) {
          // 调用wrapper的close方法,如果有的话
          wrapper.close.call(this, initData);
        }
        errorThrown = false;
      } finally {
        if (errorThrown) {
          try {
            this.closeAll(i + 1);
          } catch (e) {}
        }
      }
    }
    this.wrapperInitData.length = 0;
  }

los componentes de actualización: runBatchedUpdates

El análisis previo a la enqueueUpdatellamada transaction.perform(callback, args...)después del descubrimiento, callbacko el enqueueUpdatemétodo de ah, no lo haría 死循环él? No quiere decir que el bien setStateserá llamado updateComponentpara actualizar automáticamente Ver hacer? Hay que empezar primero con la operación u operaciones.

Nuestra envoltorio se ha registrado en dos envoltura, de la siguiente manera:

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

RESET_BATCHED_UPDATESSe utiliza para administrar el isBatchingUpdatesestado, por delante de nosotros en el análisis de si setstate efecto inmediato ha sido explicado antes.

Que FLUSH_BATCHED_UPDATESutiliza para hacerlo?

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)};
var flushBatchedUpdates = function () {
  // 循环遍历处理完所有dirtyComponents
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      // close前执行完runBatchedUpdates方法,这是关键
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }

    if (asapEnqueued) {
      asapEnqueued = false;
      var queue = asapCallbackQueue;
      asapCallbackQueue = CallbackQueue.getPooled();
      queue.notifyAll();
      CallbackQueue.release(queue);
    }
  }};

FLUSH_BATCHED_UPDATESEn una voluntad transactionde closela fase operativa runBatchedUpdates, para llevar a cabo update.

function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  dirtyComponents.sort(mountOrderComparator);

  for (var i = 0; i < len; i++) {
    // dirtyComponents中取出一个component
    var component = dirtyComponents[i];

    // 取出dirtyComponent中的未执行的callback,下面就准备执行它了
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    var markerName;
    if (ReactFeatureFlags.logTopLevelRenders) {
      var namedComponent = component;
      if (component._currentElement.props === component._renderedComponent._currentElement) {
        namedComponent = component._renderedComponent;
      }
    }
    // 执行updateComponent
    ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction);

    // 执行dirtyComponent中之前未执行的callback
    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
      }
    }
  }}

runBatchedUpdatesLoop a través de dirtyComponentsla matriz, dos cosas seque principalmente.

  1. PerformUpdateIfNecessary primera ejecución para actualizar la vista de componentes
  2. El bloqueo de ejecución antes de la devolución de llamada.

Echemos un vistazo performUpdateIfNecessary:

performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
      // receiveComponent会最终调用到updateComponent,从而刷新View
      ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
    }

    if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      // 执行updateComponent,从而刷新View。这个流程在React生命周期中讲解过
      this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
    }
  },

La última sorpresa de la sierra receiveComponenty la updateComponentbarra.

receiveComponentPor último se llamará updateComponent, y updateComponentllevará a cabo el enfoque de ciclo de vida Reaccionar delante de la congregación,

Tales como componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate.

De este modo completar el montaje de todo el proceso de actualización.

En general Proceso de Revisión:

1.enqueueSetState la opción de venta del estado en la cola, y llama proceso de enqueueUpdate para actualizar Componente
2. Si el componente se encuentra actualmente en la transacción de actualización, el primer componente se almacena en dirtyComponent. De lo contrario, la llamada batchedUpdates proceso.
3.batchedUpdates lanzan una transaction.perform () asuntos
4. inicia una transacción de inicialización, ejecución, poniendo fin a tres etapas
5. Inicialización: método de fase de inicialización La transacción no se ha registrado, por lo que no existe un método para ejecutar
6. Funcionamiento: entrante setSate ejecución método de devolución de llamada, por lo general, no pasará de devolución de llamada de parámetros
7. final: isBatchingUpdates actualización es falsa, y llevar a cabo cerca de las FLUSH_BATCHED_UPDATES método de envoltura en
8.FLUSH_BATCHED_UPDATES en estrecha etapa, un ciclo a través de todos los componentes de actualización dirtyComponents, llamada updateComponent, y ejecutarlo pendingCallbacks, setstate ocupa de devolución de llamada.

Después de leer la teoría, entonces consolidar un ejemplo.

Mira un ejemplo:

class Example extends React.Component {
 constructor() {
   super();
   this.state = {
    val: 0
   };
}
componentDidMount() {
  this.setState({val: this.state.val + 1});
  console.log('第 1 次 log:', this.state.val);
  this.setState({val: this.state.val + 1});
  console.log('第 2 次 log:', this.state.val);

 setTimeout(() => {
  this.setState({val: this.state.val + 1});
  console.log('第 3 次 log:', this.state.val);   
  this.setState({val: this.state.val + 1});
  console.log('第 4 次 log:', this.state.val); 
 }, 0);
}
 render() {
  return null;
 }
};

En los dos anteriores isBatchingUpdates, el Estado no se actualiza, dos de salida 0.

Dos veces más tarde actualizado salidas simultáneamente 2, 3;

Obviamente, podemos ser cuatro veces más reglas simples setstate 两类:

  1. componentDidMount es una clase
  2. setTimeOut pertenece a una clase porque estos dos realizan diferentes pila de llamadas.

Vamos a echar un vistazo a la pila de llamadas en componentDidMount en setstate de:

clipboard.png

Mira la pila de llamadas en setTimeOut en:

clipboard.png

Nos fijamos en el foco pila de llamadas sw3e en componentDidMount en:
encontrado un batchedUpdatesmétodo.

Antes setstate originalmente llamado, ha sido 处于batchedUpdates执行的事务之中una.

Ese batchedUpdatesmétodo es que llamarlo? Vayamos en un uno retroactiva, que resultó ser _renderNewRootComponent ReactMount.js método da.

En otras palabras, todo el conjunto va a reaccionar a proceso de representación DOM está en un gran transacción en el.

Entonces es fácil de entender: debido a que la componentDidMountllamada setStatecuando batchingStrategyel isBatchingUpdatesse ha establecido true, por lo que los resultados de los dos setstate no tienen efecto inmediato, pero se pusieron en el dirtyComponentsmedio.

Esto explica también la impresión this.state.val son dos 0razones, debido a que el nuevo estado no se ha aplicado al componente.

Mirar dos veces setstate setTimeOut en, porque no hay delante de la batchedUpdatellamada, por lo que batchingStrategyel isBatchingUpdatesbit de bandera false, también dio lugar a un nuevo stateefecto inmediatamente, no ha venido a dirtyComponentsla rama.

En otras palabras, SetTimeOut la primera actuación, el tiempo setstate, this.state.val 1;

Durante la impresión después de la finalización de setstate this.state.val se convierte en 2.

El segundo empatía setstate.

Por el ejemplo anterior, sabemos setstate es 可以同步更新, pero todavía tratamos de evitar el uso directo, ya que sólo entenderlo.

Si usted tiene que jugar alguna operación espectáculo, escribir código para ir directamente a la this.state operación:

this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.setState();

Sólo puedo decir, hermano mayor del pecho, que está muy mostrar. Tengo viejos amigos como Diao Yu, Zhang Xu hierba alta tumba ahora.

Supongo que te gusta

Origin www.cnblogs.com/geck/p/12542496.html
Recomendado
Clasificación