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 getInternalInstanceReadyForUpdate
la 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];
enqueueUpdate
Reaccionar contiene 避免重复render
la lógica.
mountComponent
Y un updateComponent
método de ejecución en un principio, será llamado a batchedUpdates
realizar 批处理
la actualización, será esta vez isBatchingUpdates
establecido 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_WRAPPERS
se incluye RESET_BATCHED_UPDATES
en esta wrapper
, con el tiempo se llame RESET_BATCHED_UPDATES.close()
, que eventualmente isBatchingUpdates
establecido false
.
Por lo tanto getInitialState
, componentWillMount
, render
, componentWillUpdate
en la setState
voluntad no causar updateComponent
.
Sin embargo, en componentDidMount
y componentDidUpdate
de la voluntad.
negocios
Transacción wrapper
encapsulado.
A wrapper
un par de contener initialize
y close
mé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];
transaction
A través de transaction.perform(callback, args…)
método para entrar, primero llame buena registrada wrapper
en el initialize
método, y luego ejecutar perform
métodos callback
, y finalmente la ejecución close
del 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 enqueueUpdate
llamada transaction.perform(callback, args...)
después del descubrimiento, callback
o el enqueueUpdate
método de ah, no lo haría 死循环
él? No quiere decir que el bien setState
será llamado updateComponent
para 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_UPDATES
Se utiliza para administrar el isBatchingUpdates
estado, por delante de nosotros en el análisis de si setstate efecto inmediato ha sido explicado antes.
Que FLUSH_BATCHED_UPDATES
utiliza 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_UPDATES
En una voluntad transaction
de close
la 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());
}
}
}}
runBatchedUpdates
Loop a través de dirtyComponents
la matriz, dos cosas seque principalmente.
- PerformUpdateIfNecessary primera ejecución para actualizar la vista de componentes
- 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 receiveComponent
y la updateComponent
barra.
receiveComponent
Por último se llamará updateComponent
, y updateComponent
llevará 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 两类
:
- componentDidMount es una clase
- 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:
Mira la pila de llamadas en setTimeOut en:
Nos fijamos en el foco pila de llamadas sw3e en componentDidMount en:
encontrado un batchedUpdates
método.
Antes setstate originalmente llamado, ha sido 处于batchedUpdates执行的事务之中
una.
Ese batchedUpdates
mé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 componentDidMount
llamada setState
cuando batchingStrategy
el isBatchingUpdates
se ha establecido true
, por lo que los resultados de los dos setstate no tienen efecto inmediato, pero se pusieron en el dirtyComponents
medio.
Esto explica también la impresión this.state.val son dos 0
razones, debido a que el nuevo estado no se ha aplicado al componente.
Mirar dos veces setstate setTimeOut en, porque no hay delante de la batchedUpdate
llamada, por lo que batchingStrategy
el isBatchingUpdates
bit de bandera false
, también dio lugar a un nuevo state
efecto inmediatamente, no ha venido a dirtyComponents
la 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.