成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

React中setState真的是異步的嗎

hankkin / 1470人閱讀

摘要:在學(xué)習(xí)的過程中幾乎所有學(xué)習(xí)材料都會反復(fù)強(qiáng)調(diào)一點是異步的來看一下官網(wǎng)對于的說明。將認(rèn)為是一次請求而不是一次立即執(zhí)行更新組件的命令??偨Y(jié)在組件生命周期中或者事件綁定中,是通過異步更新的。在延時的回調(diào)或者原生事件綁定的回調(diào)中調(diào)用不一定是異步的。

在學(xué)習(xí)react的過程中幾乎所有學(xué)習(xí)材料都會反復(fù)強(qiáng)調(diào)一點setState是異步的,來看一下react官網(wǎng)對于setState的說明。

setState()認(rèn)為是一次請求而不是一次立即執(zhí)行更新組件的命令。為了更為可觀的性能,React可能會推遲它,稍后會一次性更新這些組件。React不會保證在setState之后,能夠立刻拿到改變的結(jié)果。

一個很經(jīng)典的例子如下

// state.count 當(dāng)前為 0
componentDidMount(){
    this.setState({count: state.count + 1});
    console.log(this.state.count)
}

如果你熟悉react,你一定知道最后的輸出結(jié)果是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) ); //延時調(diào)用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節(jié)點中綁定onClick事件

在componentDidMount中手動綁定mousedown事件

在componentDidMount中使用setTimeout調(diào)用onClick

你可以猜到結(jié)果嗎?輸出結(jié)果是:

timeout
"prev state:"
0
"next state:"
1
mousedown
"prev state:"
1
"next state:"
2
click
"prev state:"
2
"next state:"
2

結(jié)果似乎有點出人意料,三種方式只有在div上綁定的onClick事件輸出了可以證明setState是異步的結(jié)果,另外兩種方式顯示setState似乎是同步的。

React的核心成員Dan Abramov也在一次回復(fù)中提到

這到底是這么回事?

話不多說,直接上源碼,如果你對react源碼有一定了解可以接著往下看,如果沒有,可以直接跳到結(jié)論(以下分析基于react15,16版本可能有出入)。

setState異步的實現(xiàn)
在componentWillMount中調(diào)用setState
//代碼位于ReactBaseClasses
 * @param {partialState} 設(shè)置的state參數(shù)
 * @param {callback} 設(shè)置state后的回調(diào)
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中調(diào)用了enqueueSetState方法將傳入的state放到一個隊列中,接下來,看下enqueueSetState的具體實現(xiàn):

//代碼位于ReactUpdateQueue.js
   * @param {publicInstance} 需要重新渲染的組件實例
   * @param {partialState} 設(shè)置的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隊列中,接下來調(diào)用了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;
  }
}

這段代碼就是實現(xiàn)setState異步更新的關(guān)鍵了,首先要了解的就是batchingStrategy,顧名思義就是批量更新策略,其中通過事務(wù)的方式實現(xiàn)state的批量更新,這里的事務(wù)和數(shù)據(jù)庫中的事務(wù)的概念類似,但不完全相同,這里就不具體展開了,有時間可以具體寫下,是react中十分重要也是很有意思的內(nèi)容。
isBatchingUpdates是該事務(wù)的一個標(biāo)志,如果為true,表示react正在一個更新組件的事務(wù)流中,根據(jù)以上代碼邏輯:

如果沒有在事務(wù)流中,調(diào)用batchedUpdates方法進(jìn)入更新流程,進(jìn)入流程后,會將isBatchingUpdates設(shè)置為true。

否則,將需更新的組件放入dirtyComponents中,也很好理解,先將需更新的組件存起來,稍后更新。

這就解釋了在componentDidMount中調(diào)用setState并不會立即更新state,因為正處于一個更新流程中,isBatchingUpdates為true,所以只會放入dirtyComponents中等待稍后更新。

事件中的調(diào)用setState

那么在事件中調(diào)用setState又為什么也是異步的呢,react是通過合成事件實現(xiàn)了對于事件的綁定,在組件創(chuàng)建和更新的入口方法mountComponent和updateComponent中會將綁定的事件注冊到document節(jié)點上,相應(yīng)的回調(diào)函數(shù)通過EventPluginHub存儲。
當(dāng)事件觸發(fā)時,document上addEventListener注冊的callback會被回調(diào)。從前面事件注冊部分發(fā)現(xiàn),此時回調(diào)函數(shù)為ReactEventListener.dispatchEvent,它是事件分發(fā)的入口方法。下面我們來看下dispatchEvent:

dispatchEvent: function (topLevelType, nativeEvent) {
    // disable了則直接不回調(diào)相關(guān)方法
    if (!ReactEventListener._enabled) {
      return;
    }

    var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent);
    try {
      // 放入
      ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
    } finally {
      TopLevelCallbackBookKeeping.release(bookKeeping);
    }
}

看到了熟悉的batchedUpdates方法,只是調(diào)用方換成了ReactUpdates,再進(jìn)入ReactUpdates.batchedUpdates。

function batchedUpdates(callback, a, b, c, d, e) {
  ensureInjected();
  return batchingStrategy.batchedUpdates(callback, a, b, c, d, e);
}

豁然開朗,原來在事件的處理中也是通過同樣的事務(wù)完成的,當(dāng)進(jìn)入事件處理流程后,該事務(wù)的isBatchingUpdates為true,如果在事件中調(diào)用setState方法,也會進(jìn)入dirtyComponent流程。

原生事件綁定和setTimeout中setState

在回過頭來看同步的情況,原生事件綁定不會通過合成事件的方式處理,自然也不會進(jìn)入更新事務(wù)的處理流程。setTimeout也一樣,在setTimeout回調(diào)執(zhí)行時已經(jīng)完成了原更新組件流程,不會放入dirtyComponent進(jìn)行異步更新,其結(jié)果自然是同步的。

順便提一下,在更新組建時,將更新的state合并到原state是在componentWillUpdate之后,render之前,所以在componentWillUpdate之前設(shè)置的setState可以在render中拿到最新值。

總結(jié)

1.在組件生命周期中或者react事件綁定中,setState是通過異步更新的。
2.在延時的回調(diào)或者原生事件綁定的回調(diào)中調(diào)用setState不一定是異步的。

這個結(jié)果并不說明setState異步執(zhí)行的說法是錯誤的,更加準(zhǔn)確的說法應(yīng)該是setState不能保證同步執(zhí)行。

Dan Abramov也多次提到今后會將setState改造為異步的,從js conf中提到的suspend新特新也印證了這一點。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/93891.html

相關(guān)文章

  • 解讀react的setSate的異步問題

    摘要:總結(jié)在組件生命周期中或者事件綁定中,是通過異步更新的。在延時的回調(diào)或者原生事件綁定的回調(diào)中調(diào)用不一定是異步的。這個結(jié)果并不說明異步執(zhí)行的說法是錯誤的,更加準(zhǔn)確的說法應(yīng)該是不能保證同步執(zhí)行。 在我們閱讀文檔的時候,大多都說react的setState是異步的,可是它真的是異步的嗎?如果是,那我們還可以猜想:那可以不可以同步?那什么時候需要異步,什么時候需要同步呢? 我們先來看下reac...

    atinosun 評論0 收藏0
  • 理解reactsetState

    摘要:組件狀態(tài)是一種持有,處理和使用信息的方式。更新唯一你能直接寫的地方應(yīng)該是組件的構(gòu)造函數(shù)中。是異步的事實上會引起的一致性處理重新渲染組件樹的過程,是下一個屬性的基礎(chǔ)即是異步的。常見錯誤其中最常見的錯誤之一就是在構(gòu)造函數(shù)中使用設(shè)置的值。 組件狀態(tài)(state)是一種持有,處理和使用信息的方式。state包含的信息僅作用于一個給定組件的內(nèi)部,并允許你根據(jù)它實現(xiàn)組件的一些邏輯。state通常是...

    FingerLiu 評論0 收藏0
  • Luy 1.0 :一個React-like輪子的誕生

    摘要:司徒正美的一款了不起的化方案,支持到。行代碼內(nèi)實現(xiàn)一個胡子大哈實現(xiàn)的作品其實就是的了源碼學(xué)習(xí)個人文章源碼學(xué)習(xí)個人文章源碼學(xué)習(xí)個人文章源碼學(xué)習(xí)個人文章這幾片文章的作者都是司徒正美,全面的解析和官方的對比。 前言 在過去的一個多月中,為了能夠更深入的學(xué)習(xí),使用React,了解React內(nèi)部算法,數(shù)據(jù)結(jié)構(gòu),我自己,從零開始寫了一個玩具框架。 截止今日,終于可以發(fā)布第一個版本,因為就在昨天,我...

    codecook 評論0 收藏0
  • 深入React知識點整理(二)

    摘要:承接上文,深入知識點整理一使用也滿一年了,從剛剛會使用到逐漸探究其底層實現(xiàn),以便學(xué)習(xí)幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。有限狀態(tài)機(jī),表示有限個狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的模型。 承接上文,深入React知識點整理(一)使用React也滿一年了,從剛剛會使用到逐漸探究其底層實現(xiàn),以便學(xué)習(xí)幾招奇技淫巧從而在自己的代碼中使用,寫出高效的代碼。下面整理一些知識點,...

    villainhr 評論0 收藏0

發(fā)表評論

0條評論

hankkin

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<