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

資訊專欄INFORMATION COLUMN

一個(gè)關(guān)于React.Component.setState的問(wèn)題

BoYang / 2422人閱讀

摘要:不保證這個(gè)狀態(tài)的更新是立即執(zhí)行的。這個(gè)問(wèn)題導(dǎo)致如果開(kāi)發(fā)者在之后立即去訪問(wèn)可能訪問(wèn)的不是最新的狀態(tài)。不應(yīng)該被直接更改,而是應(yīng)該新建一個(gè)來(lái)表示更新后的狀態(tài)。實(shí)驗(yàn)采用基于控制變量法的對(duì)照試驗(yàn)。至于的問(wèn)題,留給讀者自己吧。

React組件重新渲染的條件是:
B.只要調(diào)用this.setState()就會(huì)發(fā)生重新渲染。
C.必須調(diào)用this.setState()且傳遞不同于當(dāng)前this.setState()的參數(shù),才會(huì)引發(fā)重新渲染。

本文將從三方面說(shuō)明這個(gè)問(wèn)題為什么選擇C。或者說(shuō)為什么 setState 在傳遞不同當(dāng)前 this.State 的參數(shù),才會(huì)引發(fā)組件重新渲染。

結(jié)論

我還是想選擇B

引用規(guī)范

TL;DR

下面是 React 官方對(duì)于 setState 的說(shuō)明,翻譯的作者是我。在這段文章中,對(duì)setState說(shuō)明了兩點(diǎn)。

setState是異步的。

setState會(huì)(always)導(dǎo)致重新渲染,當(dāng)且僅當(dāng)shouldComponentUpdate()返回了false的時(shí)候不會(huì)。

讀者可以直接進(jìn)入實(shí)驗(yàn)部分。

React原文中關(guān)于setState的說(shuō)明:

setState(updater[, callback])

setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.

setState() 會(huì)將當(dāng)前組件的 state 的更改全部推入隊(duì)列,并且通知 React 這個(gè)組件和他的孩子們需要更新這些狀態(tài)并重新渲染。這是開(kāi)發(fā)者經(jīng)常使用的用來(lái)更新 UI 的方法(不管是在事件響應(yīng)中還是處理服務(wù)端的返回)。

Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

setState()當(dāng)作一個(gè)更新的_請(qǐng)求_而不是一個(gè)更新的函數(shù)。為了更好的性能,React 可能會(huì)延遲這些更新,將幾個(gè)組件的更新合并在一起執(zhí)行。React不保證這個(gè)狀態(tài)的更新是立即執(zhí)行的。

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.

setState()并不是立即更新這些組件,而是可能延后批量更新。這個(gè)問(wèn)題導(dǎo)致如果開(kāi)發(fā)者在setState()之后立即去訪問(wèn)this.state可能訪問(wèn)的不是最新的狀態(tài)。然而,開(kāi)發(fā)者還是可以使用一些方法來(lái)訪問(wèn)到最新的state的。比如在組件生命周期的componentDidUpdate,或者在setState的回調(diào)函數(shù)中。當(dāng)然了如果你需要依賴之前的狀態(tài)來(lái)更新當(dāng)前的狀態(tài),看一看這個(gè)updater。

setState() will always lead to a re-render unless shouldComponentUpdate() returns false. If mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.

setState() 肯定總是一直毫無(wú)疑問(wèn)的會(huì)導(dǎo)致render函數(shù)被重新調(diào)用[1],除非shouldComponentUpdate()返回了false。如果開(kāi)發(fā)者使用了可變的變量或者更新的邏輯無(wú)法在shouldComponentUpdate()中編寫(xiě),那為了減少無(wú)意義的重新渲染,應(yīng)該僅僅在確定當(dāng)前的新?tīng)顟B(tài)和舊狀態(tài)不一樣的時(shí)候調(diào)用setState()?!鞠Mx者不要誤會(huì),React是讓開(kāi)發(fā)者自己做這個(gè)比較。不是React替你做好了的。】

[1].(我們把這種行為叫做重新渲染)

The first argument is an updater function with the signature:

setState()可以接受兩個(gè)參數(shù),第一個(gè)參數(shù)叫做updater的函數(shù),函數(shù)的簽名如下:

(prevState, props) => stateChange

prevState is a reference to the previous state. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from prevState and props. For instance, suppose we wanted to increment a value in state by props.step:

prevState是組件之前的狀態(tài)(引用關(guān)系)。prevState不應(yīng)該被直接更改,而是應(yīng)該新建一個(gè)Object來(lái)表示更新后的狀態(tài)。舉個(gè)例子:如果開(kāi)發(fā)者想要更新state中的counter給它加一。應(yīng)該按照下面的做法。

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});

Both prevState and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with prevState.

React 保證 updater 接受的 prevStateprops 都是最新的。并且updater 的返回是被淺拷貝merge進(jìn)入老狀態(tài)的。

The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.

setState()的第二個(gè)參數(shù)是可選的回調(diào)函數(shù)。在state更新完成后他會(huì)被執(zhí)行一次??傮w上來(lái)說(shuō),React官方更推薦在componentDidUpdate()中來(lái)實(shí)現(xiàn)這個(gè)邏輯。

You may optionally pass an object as the first argument to setState() instead of a function:

開(kāi)發(fā)者還可以在第一次參數(shù)的位置不傳入函數(shù),而是傳入一個(gè)對(duì)象。

setState(stateChange[, callback])

This performs a shallow merge of stateChange into the new state, e.g., to adjust a shopping cart item quantity:

像上面這種調(diào)用方式中,stateChange會(huì)被淺拷貝進(jìn)入老狀態(tài)。例如開(kāi)發(fā)者更新購(gòu)物車中的商品數(shù)量的代碼應(yīng)該如下所示:

this.setState({quantity: 2})

This form of setState() is also asynchronous, and multiple calls during the same cycle may be batched together. For example, if you attempt to increment an item quantity more than once in the same cycle, that will result in the equivalent of:

這種形式的setState()也是異步的,而且在一個(gè)周期內(nèi)的多次更新會(huì)被批量一起更新。如果你想更新?tīng)顟B(tài)里面的數(shù)量,讓他一直加一加一。代碼就會(huì)如下所示

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the previous state, we recommend using the updater function form, instead:

這樣隊(duì)列的調(diào)用會(huì)重寫(xiě)之前的更新,所以最后數(shù)量?jī)H僅會(huì)被更新一次。在這種新?tīng)顟B(tài)依賴?yán)蠣顟B(tài)數(shù)據(jù)的情況下,React官方推薦大家使用函數(shù)。如下所示:

this.setState((prevState) => {
  return {quantity: prevState.quantity + 1};
});
實(shí)驗(yàn)驗(yàn)證

設(shè)計(jì)實(shí)驗(yàn)來(lái)驗(yàn)證React官方說(shuō)法的正確性。實(shí)驗(yàn)采用基于控制變量法的對(duì)照試驗(yàn)。

基于 React16( 引入了 Fiber 架構(gòu))和 React 0.14 分別進(jìn)行實(shí)驗(yàn)。至于React 15的問(wèn)題,留給讀者自己吧。

編寫(xiě)如下組件代碼:

class A extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            a:1
        }
        this._onClick = this.onClick.bind(this);
    }
    onClick(){
        this.setState({a:2}) // 替換點(diǎn)
    }
    render(){
        console.log("rerender");
        return(
            

a: {this.state.a}

{Math.random()}

); } }

如果需要可以讀者自行粘貼重新復(fù)現(xiàn)實(shí)驗(yàn)。

更新的標(biāo)準(zhǔn):界面中顯示的隨機(jī)數(shù)是否發(fā)生了變化。當(dāng)然也可以觀察Console中是否出現(xiàn)了rerender。

React 0.14.5 實(shí)驗(yàn)結(jié)果如下所示:

條件 不編寫(xiě)shouldComponentUpdate()方法 return false; return true;
setState({}) 更新 不更新 更新
setState(null) 更新 不更新 更新
setState(undefined) 更新 不更新 更新
setState(this.state) 更新 不更新 更新
setState(s=>s) 更新 不更新 更新
setState({a:2}) 更新 不更新 更新

React 16 實(shí)驗(yàn)結(jié)果如下所示:

條件 不編寫(xiě)shouldComponentUpdate()方法 return false; return true;
setState({}) 更新 不更新 更新
setState(null) 不更新 不更新 不更新
setState(undefined) 不更新 不更新 不更新
setState(this.state) 更新 不更新 更新
setState(s=>s) 更新 不更新 更新
setState({a:2}) 更新 不更新 更新

可見(jiàn)對(duì)于setState()來(lái)說(shuō),React 在不同版本的表現(xiàn)不盡相同。

React 0.14中可能更符合只要調(diào)用setState()就會(huì)進(jìn)行更新。

React 16.3.2中只有在傳遞null和undefined的時(shí)候才不會(huì)更新,別的時(shí)候都更新。

源碼說(shuō)明 React 16中是這樣的:

https://github.com/facebook/r...

1. const payload = update.payload;
2. let partialState;
3. if (typeof payload === "function") {
4.       partialState = payload.call(instance, prevState, nextProps);
5. } else {
6.       // Partial state object
7.       partialState = payload;
8. }
9. if (partialState === null || partialState === undefined) {
10.   // Null and undefined are treated as no-ops.
11.   return prevState;
12.}
13.// Merge the partial state and the previous state.
14.return Object.assign({}, prevState, partialState);
React 14中是這樣的:
證有容易,證無(wú)難,所以我要順著整條鏈路的源碼的展示一遍。

TL;DR

var nextState = assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
   var partial = queue[i];
   assign(nextState, typeof partial === "function" ? partial.call(inst, nextState, props, context) : partial);
}
return nextState;

流程中沒(méi)有任何比較操作。

1.調(diào)用

setState({})

2.原型方法

ReactComponent.prototype.setState = function (partialState, callback) {
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback);
  }
};

3.入隊(duì)方法

enqueueSetState: function (publicInstance, partialState) {
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, "setState");
    if (!internalInstance) {
      return;
    }
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);
    enqueueUpdate(internalInstance);
  },

internalInstance 是一個(gè) ReactCompositeComponentWrapper,大概就是包裝著ReactComponent實(shí)例的一個(gè)對(duì)象。

4.入隊(duì)

function enqueueUpdate(internalInstance) {
  ReactUpdates.enqueueUpdate(internalInstance);
}

5.更具當(dāng)前的批量策略來(lái)決定更新方法

function enqueueUpdate(component) {
  ensureInjected();

  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

  dirtyComponents.push(component);
}

6.可以看到直到這里都沒(méi)人管這個(gè)東西到底更新的是什么。
7.剩下的事情基本就是垃圾回收處理現(xiàn)場(chǎng)的事情了。
8.處理完之后會(huì)

ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);

9.請(qǐng)求更新隊(duì)列,進(jìn)行更新

var flushBatchedUpdates = function () {
  // ReactUpdatesFlushTransaction"s wrappers will clear the dirtyComponents
  // array and perform any updates enqueued by mount-ready handlers (i.e.,
  // componentDidUpdate) but we need to check here too in order to catch
  // updates enqueued by setState callbacks and asap calls.
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }

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

10.更新

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

  // Since reconciling a component higher in the owner hierarchy usually (not
  // always -- see shouldComponentUpdate()) will reconcile children, reconcile
  // them before their children by sorting the array.
  dirtyComponents.sort(mountOrderComparator);

  for (var i = 0; i < len; i++) {
    // If a component is unmounted before pending changes apply, it will still
    // be here, but we assume that it has cleared its _pendingCallbacks and
    // that performUpdateIfNecessary is a noop.
    var component = dirtyComponents[i];

    // If performUpdateIfNecessary happens to enqueue any new updates, we
    // shouldn"t execute the callbacks until the next render happens, so
    // stash the callbacks first
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction);

    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
      }
    }
  }
}

11.最重要的來(lái)了

performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
      ReactReconciler.receiveComponent(this, this._pendingElement || this._currentElement, transaction, this._context);
    }

    if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
    }
  },

12.更新

updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {
    //... props context 更新
    var nextState = this._processPendingState(nextProps, nextContext);

    var shouldUpdate = this._pendingForceUpdate || !inst.shouldComponentUpdate || inst.shouldComponentUpdate(nextProps, nextState, nextContext);

    if (shouldUpdate) {
      this._pendingForceUpdate = false;
      // Will set `this.props`, `this.state` and `this.context`.
      this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);
    } else {
      // If it"s determined that a component should not update, we still want
      // to set props and state but we shortcut the rest of the update.
      this._currentElement = nextParentElement;
      this._context = nextUnmaskedContext;
      inst.props = nextProps;
      inst.state = nextState;
      inst.context = nextContext;
    }
  },

13.計(jì)算state

_processPendingState: function (props, context) {
    var inst = this._instance;
    var queue = this._pendingStateQueue;
    var replace = this._pendingReplaceState;
    this._pendingReplaceState = false;
    this._pendingStateQueue = null;

    if (!queue) {
      return inst.state;
    }

    if (replace && queue.length === 1) {
      return queue[0];
    }

    var nextState = assign({}, replace ? queue[0] : inst.state);
    for (var i = replace ? 1 : 0; i < queue.length; i++) {
      var partial = queue[i];
      assign(nextState, typeof partial === "function" ? partial.call(inst, nextState, props, context) : partial);
    }

    return nextState;
  },

14.就這樣了。

var nextState = assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
   var partial = queue[i];
   assign(nextState, typeof partial === "function" ? partial.call(inst, nextState, props, context) : partial);
}
return nextState;

15.流程中沒(méi)有任何比較操作。

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

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

相關(guān)文章

  • React入門0x006: State

    摘要:概述這一章講,是的核心,也算是的核心思想都很核心啊。但是接著我們又搞了一個(gè)定時(shí)器,每秒執(zhí)行一直函數(shù),將修改為最新的時(shí)間。就完成了視圖的更新。參數(shù)一是要更新的數(shù)據(jù),我們只需要傳輸我們要更新的數(shù)據(jù)即可,對(duì)于不需要更新的數(shù)據(jù),則不需要理睬。 0x000 概述 這一章講state,state是MVVM的核心,也算是React的核心思想......都很核心啊。 0x001 問(wèn)題 在上一章節(jié)的栗子...

    TNFE 評(píng)論0 收藏0
  • 關(guān)于個(gè)人開(kāi)源項(xiàng)目(vue app)一些總結(jié)

    摘要:關(guān)于個(gè)人開(kāi)源項(xiàng)目的一些總結(jié)項(xiàng)目地址項(xiàng)目簡(jiǎn)介此項(xiàng)目名叫。網(wǎng)站目前實(shí)現(xiàn)了登錄注冊(cè)日歷導(dǎo)入文件考勤導(dǎo)出缺勤名單等核心功能。這對(duì)于小型項(xiàng)目來(lái)說(shuō)并沒(méi)有什么問(wèn)題。編譯后的大小關(guān)于文件上傳與導(dǎo)出功能文件上傳導(dǎo)出可以說(shuō)是此項(xiàng)目最關(guān)鍵的點(diǎn)了。 關(guān)于個(gè)人開(kāi)源項(xiàng)目(vue app)的一些總結(jié) 項(xiàng)目地址 https://github.com/BYChoo/record 項(xiàng)目簡(jiǎn)介 此項(xiàng)目名叫:Record。是以...

    since1986 評(píng)論0 收藏0
  • 關(guān)于個(gè)人開(kāi)源項(xiàng)目(vue app)一些總結(jié)

    摘要:關(guān)于個(gè)人開(kāi)源項(xiàng)目的一些總結(jié)項(xiàng)目地址項(xiàng)目簡(jiǎn)介此項(xiàng)目名叫。網(wǎng)站目前實(shí)現(xiàn)了登錄注冊(cè)日歷導(dǎo)入文件考勤導(dǎo)出缺勤名單等核心功能。這對(duì)于小型項(xiàng)目來(lái)說(shuō)并沒(méi)有什么問(wèn)題。編譯后的大小關(guān)于文件上傳與導(dǎo)出功能文件上傳導(dǎo)出可以說(shuō)是此項(xiàng)目最關(guān)鍵的點(diǎn)了。 關(guān)于個(gè)人開(kāi)源項(xiàng)目(vue app)的一些總結(jié) 項(xiàng)目地址 https://github.com/BYChoo/record 項(xiàng)目簡(jiǎn)介 此項(xiàng)目名叫:Record。是以...

    高勝山 評(píng)論0 收藏0
  • 2017年五月前端面試題目總結(jié)

    摘要:持續(xù)心累的找工作階段算是結(jié)束了,不同公司對(duì)面試的知識(shí)側(cè)重點(diǎn)不同,整體的感受就是大公司可能更偏向一些基礎(chǔ)或者原理布局一些經(jīng)典算法方面?,F(xiàn)將我在面試過(guò)程遇到的問(wèn)題總結(jié)下。目前先傳題目答案整理好之后再發(fā)布出來(lái)。 持續(xù)心累的找工作階段算是結(jié)束了,不同公司對(duì)面試的知識(shí)側(cè)重點(diǎn)不同,整體的感受就是:大公司可能更偏向一些JS基礎(chǔ)或者原理、html布局、一些經(jīng)典算法方面。小公司的面試更加側(cè)重對(duì)經(jīng)驗(yàn)和細(xì)節(jié)...

    warkiz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<