SETSTATEソース
メソッドのエントリ
// 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);}
前記getInternalInstanceReadyForUpdate
ソースとして
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;}
enqueueUpdateのソースコードは次のよう:
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
含まれている反応する避免重复render
ロジックを。
mountComponent
そして、updateComponent
初めに実行する方法は、ために呼び出されるbatchedUpdates
実行する批处理
アップデートを、この時間はされますisBatchingUpdates
しtrue
、状態が今のようにマークされていること、正处于更新阶段
。
トランザクションがwrapper.closeを呼び出した後の後、コンポーネントの更新を処理する方法トランザクションを反応させます()。
そして、TRANSACTION_WRAPPERS
含まれRESET_BATCHED_UPDATES
、この中でwrapper
、それが最終的に呼び出されますRESET_BATCHED_UPDATES.close()
、それは最終的になり、isBatchingUpdates
設定しますfalse
。
したがってgetInitialState
、componentWillMount
、render
、componentWillUpdate
にsetState
意志はありませupdateComponent
。
しかし、中componentDidMount
とcomponentDidUpdate
意志の。
業務
トランザクションは、wrapper
カプセル化されました。
wrapper
含有の一対initialize
とclose
する方法。例えばRESET_BATCHED_UPDATES
:
var RESET_BATCHED_UPDATES = {
// 初始化调用
initialize: emptyFunction,
// 事务执行完成,close时调用
close: function () {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}};
transcationは、次のような、包装に包装されます。
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
transaction
transaction.perform(callback, args…)
入力する方法、それは最初に登録した良いを呼び出しますwrapper
にinitialize
する方法、そして実行perform
方法をcallback
、そして最終的に実行close
する方法。
以下の分析transaction.perform(コールバック、引数...)
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;
}
アップデートコンポーネント: runBatchedUpdates
以前の分析enqueueUpdate
呼び出しtransaction.perform(callback, args...)
発見した後、callback
またはenqueueUpdate
方法ああ、ないでしょう死循环
それは?その良いと言うまでもありませんsetState
と呼ばれるupdateComponent
、自動的にビューをリフレッシュしませんか?私たちは、最初のトランザクションやトランザクションを開始する必要があります。
次のように私たちのラッパーは、2つのラッパーに登録されています。
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
RESET_BATCHED_UPDATES
管理するために使用されるisBatchingUpdates
状態を、我々の前にするかどうかSETSTATE即効性の分析では、前に説明しました。
ことは、FLUSH_BATCHED_UPDATES
それをやってするために使用しますか?
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
意志でtransaction
のclose
運用段階runBatchedUpdates
、実行します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
ループdirtyComponents
配列は、二つのことは主に乾燥させます。
- コンポーネントビューをリフレッシュするために最初に実行performUpdateIfNecessary
- コールバックの前に、実行をブロックします。
見てみましょう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);
}
},
最後の驚きソーreceiveComponent
とupdateComponent
バー。
receiveComponent
最後に、それが呼び出すupdateComponent
と、updateComponent
アセンブリの存在を反応させるのライフサイクル・アプローチを行います、
以下のようなcomponentWillReceiveProps
、shouldComponentUpdate
、componentWillUpdate
、render
、componentDidUpdate
。
したがって、全体の更新プロセスの組み立てを完了する。
全体のプロセスレビュー:
キューに1.enqueueSetState状態入れ、コンポーネント更新するenqueueUpdateプロセスを呼び出す
コンポーネントは、更新トランザクションに現在ある場合には2を、第一の成分はdirtyComponentに格納されています。それ以外の場合は、コールbatchedUpdatesを処理します。
3.batchedUpdatesは()業務transaction.performを起動
4.三つの段階を終了、トランザクションの初期化、実行を開始します
。5.初期化:トランザクション初期化フェーズ方法はとても実行する方法がない、登録されていない
着信実行setSate:6.ファイル名を指定して実行をコールバックメソッドは、一般的にパラメータコールバックを通過しないであろう
更新isBatchingUpdatesが偽であり、中近いラッパー方法FLUSH_BATCHED_UPDATESを実行します。7.終了
全てdirtyComponents、呼updateComponentリフレッシュコンポーネントを介し近い段階、意志サイクルにおける8.FLUSH_BATCHED_UPDATES、それを実行しますpendingCallbacksは、SETSTATEはコールバックに設定されています。
理論を読んだ後、私たちは、一例で固めます。
例を見てください:
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;
}
};
前の二つのではisBatchingUpdates
、国家は、二つの出力0に更新されません。
二回以降の更新を同時に3、2出力します。
明らかに、我々は、SETSTATEシンプルなルールとして4倍にすることができます两类
。
- componentDidMountはクラスであります
- これら二つの異なるコールスタックを実行するためのsetTimeoutはクラスです。
のがのSETSTATEでcomponentDidMountでコールスタックを見てみましょう。
setTimeoutメソッドのコールスタック内を見てください:
:私たちは、中componentDidMountにフォーカスsw3eのコールスタックを見
たbatchedUpdates
方法。
SETSTATEはもともと呼ばれる前に、それがされています处于batchedUpdates执行的事务之中
。
そのbatchedUpdates
方法は、それを呼び出すために誰ですか?私たちは中_renderNewRootComponent法ReactMount.jsであることが判明遡及1、に行ってみましょう。
言い換えれば、アセンブリ全体は、DOMレンダリング処理に反応するの大きい取引です。
そして、それは理解しやすいですので、componentDidMount
呼び出しsetState
時に設定されていた2 SETSTATEの結果はすぐには反映されませんでしたので、しかし、中に入れた真ん中。batchingStrategy
isBatchingUpdates
true
dirtyComponents
また、これは2つのですthis.state.val印刷について説明し0
、新しい状態がコンポーネントに適用されていないので、その理由を。
のないフロントがないため、二回SETSTATEのsetTimeoutでを見batchedUpdate
て、コールは、フラグビットも新しいにつながった、効果はすぐに来なかったブランチ。batchingStrategy
isBatchingUpdates
false
state
dirtyComponents
換言すれば、最初のパフォーマンス、SETSTATE時間のsetTimeout、1 this.state.val。
this.state.val SETSTATEの完了後に印刷中2となります。
二SETSTATEの共感。
上記の例では、我々は、SETSTATEがある知っている可以同步更新
が、それでもだけそれを理解し、直接使用を避けるようにしてください。
あなたには、いくつかのショーの運転をプレイしている場合は、書き込みコードが動作this.stateに直接移動するには:
this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.state.count = this.state.count + 1;
this.setState();
私はあなたが非常に表示され、大きな胸の兄弟を言うことができます。私は今ディアオゆう、張徐高い草の墓のような古い友人がいます。