摘要:在學習的過程中幾乎所有學習材料都會反復強調一點是異步的來看一下官網對于的說明。將認為是一次請求而不是一次立即執行更新組件的命令。總結在組件生命周期中或者事件綁定中,是通過異步更新的。在延時的回調或者原生事件綁定的回調中調用不一定是異步的。
在學習react的過程中幾乎所有學習材料都會反復強調一點setState是異步的,來看一下react官網對于setState的說明。
將setState()認為是一次請求而不是一次立即執行更新組件的命令。為了更為可觀的性能,React可能會推遲它,稍后會一次性更新這些組件。React不會保證在setState之后,能夠立刻拿到改變的結果。
一個很經典的例子如下
// state.count 當前為 0 componentDidMount(){ this.setState({count: state.count + 1}); console.log(this.state.count) }
如果你熟悉react,你一定知道最后的輸出結果是0,而不是1。
然而事實真的是這樣嗎?
我們再來看一個例子
class Hello extends Component { constructor(props) { super(props); this.state = { counter: 0 }; } render() { return點我; } componentDidMount() { //手動綁定mousedown事件 ReactDom.findDOMNode(this).addEventListener( "mousedown", this.onClick.bind(this) ); //延時調用onclick事件 setTimeout(this.onClick.bind(this), 1000); } onClick(event) { if (event) { console.log(event.type); } else { console.log("timeout"); } console.log("prev state:", this.state.counter); this.setState({ counter: this.state.counter + 1 }); console.log("next state:", this.state.counter); } } export default Hello;
在這個組件中采用3中方法更新state
在div節點中綁定onClick事件
在componentDidMount中手動綁定mousedown事件
在componentDidMount中使用setTimeout調用onClick
你可以猜到結果嗎?輸出結果是:
timeout "prev state:" 0 "next state:" 1 mousedown "prev state:" 1 "next state:" 2 click "prev state:" 2 "next state:" 2
結果似乎有點出人意料,三種方式只有在div上綁定的onClick事件輸出了可以證明setState是異步的結果,另外兩種方式顯示setState似乎是同步的。
React的核心成員Dan Abramov也在一次回復中提到
這到底是這么回事?
話不多說,直接上源碼,如果你對react源碼有一定了解可以接著往下看,如果沒有,可以直接跳到結論(以下分析基于react15,16版本可能有出入)。
setState異步的實現//代碼位于ReactBaseClasses * @param {partialState} 設置的state參數 * @param {callback} 設置state后的回調 ReactComponent.prototype.setState = function(partialState, callback) { invariant( typeof partialState === "object" || typeof partialState === "function" || partialState == null, "setState(...): takes an object of state variables to update or a " + "function which returns an object of state variables.", ); this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, "setState"); } };
在setState中調用了enqueueSetState方法將傳入的state放到一個隊列中,接下來,看下enqueueSetState的具體實現:
//代碼位于ReactUpdateQueue.js * @param {publicInstance} 需要重新渲染的組件實例 * @param {partialState} 設置的state * @internal enqueueSetState: function(publicInstance, partialState) { //省略部分代碼 //從組件列表中找到并返回需渲染的組件 var internalInstance = getInternalInstanceReadyForUpdate( publicInstance, "setState", ); if (!internalInstance) { return; } //state隊列 var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); //將新的state放入隊列 queue.push(partialState); enqueueUpdate(internalInstance); },
在enqueueSetState中先是找到需渲染組件并將新的state并入該組件的需更新的state隊列中,接下來調用了enqueueUpdate方法,接著來看:
//代碼位于ReactUpdateQueue.js function enqueueUpdate(internalInstance) { ReactUpdates.enqueueUpdate(internalInstance); } //代碼位于ReactUpdates.js function enqueueUpdate(component) { ensureInjected(); // Various parts of our code (such as ReactCompositeComponent"s // _renderValidatedComponent) assume that calls to render aren"t nested; // verify that that"s the case. (This is called by each top-level update // function, like setState, forceUpdate, etc.; creation and // destruction of top-level components is guarded in ReactMount.) if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } dirtyComponents.push(component); if (component._updateBatchNumber == null) { component._updateBatchNumber = updateBatchNumber + 1; } }
這段代碼就是實現setState異步更新的關鍵了,首先要了解的就是batchingStrategy,顧名思義就是批量更新策略,其中通過事務的方式實現state的批量更新,這里的事務和數據庫中的事務的概念類似,但不完全相同,這里就不具體展開了,有時間可以具體寫下,是react中十分重要也是很有意思的內容。
isBatchingUpdates是該事務的一個標志,如果為true,表示react正在一個更新組件的事務流中,根據以上代碼邏輯:
如果沒有在事務流中,調用batchedUpdates方法進入更新流程,進入流程后,會將isBatchingUpdates設置為true。
否則,將需更新的組件放入dirtyComponents中,也很好理解,先將需更新的組件存起來,稍后更新。
這就解釋了在componentDidMount中調用setState并不會立即更新state,因為正處于一個更新流程中,isBatchingUpdates為true,所以只會放入dirtyComponents中等待稍后更新。
那么在事件中調用setState又為什么也是異步的呢,react是通過合成事件實現了對于事件的綁定,在組件創建和更新的入口方法mountComponent和updateComponent中會將綁定的事件注冊到document節點上,相應的回調函數通過EventPluginHub存儲。
當事件觸發時,document上addEventListener注冊的callback會被回調。從前面事件注冊部分發現,此時回調函數為ReactEventListener.dispatchEvent,它是事件分發的入口方法。下面我們來看下dispatchEvent:
dispatchEvent: function (topLevelType, nativeEvent) { // disable了則直接不回調相關方法 if (!ReactEventListener._enabled) { return; } var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent); try { // 放入 ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); } finally { TopLevelCallbackBookKeeping.release(bookKeeping); } }
看到了熟悉的batchedUpdates方法,只是調用方換成了ReactUpdates,再進入ReactUpdates.batchedUpdates。
function batchedUpdates(callback, a, b, c, d, e) { ensureInjected(); return batchingStrategy.batchedUpdates(callback, a, b, c, d, e); }
豁然開朗,原來在事件的處理中也是通過同樣的事務完成的,當進入事件處理流程后,該事務的isBatchingUpdates為true,如果在事件中調用setState方法,也會進入dirtyComponent流程。
在回過頭來看同步的情況,原生事件綁定不會通過合成事件的方式處理,自然也不會進入更新事務的處理流程。setTimeout也一樣,在setTimeout回調執行時已經完成了原更新組件流程,不會放入dirtyComponent進行異步更新,其結果自然是同步的。
順便提一下,在更新組建時,將更新的state合并到原state是在componentWillUpdate之后,render之前,所以在componentWillUpdate之前設置的setState可以在render中拿到最新值。
總結1.在組件生命周期中或者react事件綁定中,setState是通過異步更新的。
2.在延時的回調或者原生事件綁定的回調中調用setState不一定是異步的。
這個結果并不說明setState異步執行的說法是錯誤的,更加準確的說法應該是setState不能保證同步執行。
Dan Abramov也多次提到今后會將setState改造為異步的,從js conf中提到的suspend新特新也印證了這一點。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93891.html
摘要:總結在組件生命周期中或者事件綁定中,是通過異步更新的。在延時的回調或者原生事件綁定的回調中調用不一定是異步的。這個結果并不說明異步執行的說法是錯誤的,更加準確的說法應該是不能保證同步執行。 在我們閱讀文檔的時候,大多都說react的setState是異步的,可是它真的是異步的嗎?如果是,那我們還可以猜想:那可以不可以同步?那什么時候需要異步,什么時候需要同步呢? 我們先來看下reac...
摘要:組件狀態是一種持有,處理和使用信息的方式。更新唯一你能直接寫的地方應該是組件的構造函數中。是異步的事實上會引起的一致性處理重新渲染組件樹的過程,是下一個屬性的基礎即是異步的。常見錯誤其中最常見的錯誤之一就是在構造函數中使用設置的值。 組件狀態(state)是一種持有,處理和使用信息的方式。state包含的信息僅作用于一個給定組件的內部,并允許你根據它實現組件的一些邏輯。state通常是...
摘要:司徒正美的一款了不起的化方案,支持到。行代碼內實現一個胡子大哈實現的作品其實就是的了源碼學習個人文章源碼學習個人文章源碼學習個人文章源碼學習個人文章這幾片文章的作者都是司徒正美,全面的解析和官方的對比。 前言 在過去的一個多月中,為了能夠更深入的學習,使用React,了解React內部算法,數據結構,我自己,從零開始寫了一個玩具框架。 截止今日,終于可以發布第一個版本,因為就在昨天,我...
摘要:承接上文,深入知識點整理一使用也滿一年了,從剛剛會使用到逐漸探究其底層實現,以便學習幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。有限狀態機,表示有限個狀態以及在這些狀態之間的轉移和動作等行為的模型。 承接上文,深入React知識點整理(一)使用React也滿一年了,從剛剛會使用到逐漸探究其底層實現,以便學習幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識點,...
閱讀 3209·2021-11-12 10:36
閱讀 1258·2019-08-30 15:56
閱讀 2444·2019-08-30 11:26
閱讀 551·2019-08-29 13:00
閱讀 3609·2019-08-28 18:08
閱讀 2749·2019-08-26 17:18
閱讀 1893·2019-08-26 13:26
閱讀 2432·2019-08-26 11:39