欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React中setState同步異步場(chǎng)景的使用

 更新時(shí)間:2022年03月21日 11:17:15   作者:WindrunnerMax  
本文主要介紹了React中setState同步異步場(chǎng)景的使用,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

setState同步異步場(chǎng)景

React通過(guò)this.state來(lái)訪問(wèn)state,通過(guò)this.setState()方法來(lái)更新state,當(dāng)this.setState()方法被調(diào)用的時(shí)候,React會(huì)重新調(diào)用render方法來(lái)重新渲染UI。相比較于在使用Hooks完成組件下所需要的心智負(fù)擔(dān),setState就是在使用class完成組件下所需要的心智負(fù)擔(dān),當(dāng)然所謂的心智負(fù)擔(dān)也許叫做所必須的基礎(chǔ)知識(shí)更加合適一些。

描述

setState只在合成事件和生命周期鉤子函數(shù)中是異步的,而在原生事件中都是同步的,簡(jiǎn)單實(shí)現(xiàn)一個(gè)React Class TS例子。

import React from "react";
import "./styles.css";

interface Props{}
interface State{
  count1: number;
  count2: number;
  count3: number;
}
export default class App extends React.Component<Props, State>{
  constructor(props: Props){
    super(props);
    this.state = {
      count1: 0,
      count2: 0,
      count3: 0
    }
  }
  
  incrementAsync = () => {
    console.log("incrementAsync before setState", this.state.count1);
    this.setState({
      count1: this.state.count1 + 1
    });
    console.log("incrementAsync after setState", this.state.count1);
  }
  incrementSync = () => {
    setTimeout(() => {
      console.log("incrementSync before setState", this.state.count2);
      this.setState({
        count2: this.state.count2 + 1
      });
      console.log("incrementSync after setState", this.state.count2);
    },0);
  }
  incrementAsyncFn = () => {
    console.log("incrementAsyncFn before setState", this.state.count3);
    this.setState({
        count3: this.state.count3 + 1
        },
        () => {
            console.log("incrementAsyncFn after.1 setState", this.state.count3);
        }
    );
    this.setState(state => {
      console.log("incrementAsyncFn after.2 setState", state.count3);
      return {count3: state.count3}
    });
  }
  render(){
    return <div>
      <button onClick={this.incrementAsync}>異步</button>
      <button onClick={this.incrementSync}>同步</button>
      <button onClick={this.incrementAsyncFn}>異步函數(shù)參數(shù)</button>
    </div>
  }
}

以此點(diǎn)擊三個(gè)按鈕的話,可以得到以下輸出。

incrementAsync before setState 0
incrementAsync after setState 0
incrementSync before setState 0
incrementSync after setState 1
incrementAsyncFn before setState 0
incrementAsyncFn after.2 setState 1
incrementAsyncFn after.1 setState 1

首先看incrementAsync的結(jié)果,在這里我們可以看出,在合成事件調(diào)用setState之后,this.state是無(wú)法立即得到最新的值。
對(duì)于incrementSync的結(jié)果,在非合成事件的調(diào)用時(shí),this.state是可以立即得到最新的值的,例如使用addEventListener、setTimeout、setInterval等。
對(duì)于incrementAsyncFn的兩個(gè)結(jié)果,首先來(lái)說(shuō)after.2的結(jié)果,對(duì)于this.state也是可以得到最新的值的,如果你需要基于當(dāng)前的state來(lái)計(jì)算出新的值,那么就可以通過(guò)傳遞一個(gè)函數(shù),而不是一個(gè)對(duì)象的方式來(lái)實(shí)現(xiàn),因?yàn)?code>setState的調(diào)用是分批的,所以通過(guò)傳遞函數(shù)可以鏈?zhǔn)降剡M(jìn)行更新,當(dāng)然前提是需要確保它們是一個(gè)建立在另一個(gè)之上的,也就是說(shuō)傳遞函數(shù)的setState的值是依賴于上次一的SetState的,對(duì)于after.1的結(jié)果,setState函數(shù)的第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),在setState批量更新完成后可以拿到最新的值,而after.2也是屬于批量更新需要調(diào)用的函數(shù),所以after.1會(huì)在after.2后執(zhí)行。

原理

React將其實(shí)現(xiàn)為異步的動(dòng)機(jī)主要是性能的考量,setState的異步并不是說(shuō)內(nèi)部由異步代碼實(shí)現(xiàn),其實(shí)本身執(zhí)行的過(guò)程和代碼都是同步的,只是合成事件和生命周期鉤子函數(shù)的調(diào)用順序在批處理更新之前,導(dǎo)致在合成事件和生命周期鉤子函數(shù)中沒法立馬拿到更新后的值,形式了所謂的異步,實(shí)際上是否進(jìn)行批處理是由其內(nèi)部的isBatchingUpdates的值來(lái)決定的。
setState依賴于合成事件,合成事件指的是React并不是將click等事件直接綁定在DOM上面,而是采用事件冒泡的形式冒泡到頂層DOM上,類似于事件委托,然后React將事件封裝給正式的函數(shù)處理運(yùn)行和處理。說(shuō)完了合成事件再回到setState,setState的批量更新優(yōu)化也是建立在合成事件上的,其會(huì)將所有的setState進(jìn)行批處理,如果對(duì)同一個(gè)值進(jìn)行多次 setState,setState的批量更新策略會(huì)對(duì)其進(jìn)行覆蓋,取最后一次的執(zhí)行,如果是同時(shí)setState多個(gè)不同的值,在更新時(shí)也會(huì)對(duì)其進(jìn)行合并批量更新,而在原生事件中,值會(huì)立即進(jìn)行更新。
采用批量更新,簡(jiǎn)單來(lái)說(shuō)就是為了提升性能,因?yàn)椴徊捎门扛?,在每次更新?shù)據(jù)都會(huì)對(duì)組件進(jìn)行重新渲染,舉個(gè)例子,讓我們?cè)谝粋€(gè)方法內(nèi)重復(fù)更新一個(gè)值。

this.setState({ msg: 1 });
this.setState({ msg: 2 });
this.setState({ msg: 3 });

事實(shí)上,我們真正想要的其實(shí)只是最后一次更新而已,也就是說(shuō)前三次更新都是可以省略的,我們只需要等所有狀態(tài)都修改好了之后再進(jìn)行渲染就可以減少一些性能損耗。還有一個(gè)例子,如果更改了N個(gè)狀態(tài),其實(shí)只需要一次setState就可以將DOM更新到最新,如果我們更新多個(gè)值。

this.setState({ msg: 1 });
this.setState({ age: 2 });
this.setState({ name: 3 });

此處我們分三次修改了三種狀態(tài),但其實(shí)React只需要渲染一次,在setState批處理之后會(huì)將其合并,并進(jìn)行一次re-render就可以將整個(gè)組件的DOM更新到最新,根本不需要關(guān)心這個(gè)setState到底是從哪個(gè)具體的狀態(tài)發(fā)出來(lái)的。

那么還有一個(gè)問(wèn)題,首先我們可以認(rèn)同進(jìn)行批處理更新對(duì)我們的性能是有益的,例如ChildParent都調(diào)用setState,我們不需要重新渲染Child兩次。但是此時(shí)我們可能會(huì)想到一個(gè)問(wèn)題,為什么不能如同Vue一樣,Vue是在值更新之后觸發(fā)setter然后進(jìn)行更新,更新的過(guò)程同樣也是采用異步渲染,也會(huì)將所有觸發(fā)Watcherupdate進(jìn)行去重合并再去更新視圖,也就是說(shuō)Vue是立即修改了值,而后再去更新視圖的。也就是說(shuō),相比較于React,為什么不能在同樣做批處理的情況下,立即將setState更新寫入this.state而不等待協(xié)調(diào)結(jié)束。

任何一種解決方案都有權(quán)衡,對(duì)于Vue來(lái)說(shuō)因?yàn)槠涫峭ㄟ^(guò)劫持了數(shù)據(jù)的setter過(guò)程,在使用的也是直接使用=直接賦值的,而在賦值之后進(jìn)行視圖的更新也是一個(gè)自然的過(guò)程,如果類似于React一樣在=之后這個(gè)值依然沒有變化,在直覺上是非常不符合常理的,雖然Vue是通過(guò)劫持setter實(shí)現(xiàn)的視圖更新,但是做到如同React一樣并不是不可能的,這是Vue采用的解決方案上的權(quán)衡,當(dāng)然這只是可能的一個(gè)理由,說(shuō)是問(wèn)題的權(quán)衡,實(shí)際上還是需要對(duì)比React來(lái)看,對(duì)于React中要處理的問(wèn)題,Vue自然會(huì)有自己解決方案的權(quán)衡,歸根到底還是框架的設(shè)計(jì)哲學(xué)的問(wèn)題。對(duì)于上面提出的在同樣做批處理的情況下,立即將setState更新寫入this.state而不等待協(xié)調(diào)結(jié)束的這個(gè)問(wèn)題,dan給予了兩個(gè)理由,在此簡(jiǎn)作總結(jié),完整的英文版本還請(qǐng)看參考中的github issue。

保證內(nèi)部數(shù)據(jù)統(tǒng)一

即使state是同步更新的,但props是不會(huì)的,在重新渲染父組件之前,無(wú)法知道props,如果同步執(zhí)行此操作,批處理就會(huì)消失?,F(xiàn)在React提供的對(duì)象state、props、refs在內(nèi)部是一致的。這意味著如果只使用這些對(duì)象,則可以保證它們引用完全協(xié)調(diào)的樹,即使它是該樹的舊版本。當(dāng)僅使用state時(shí),同步刷新的模式將起作用。

console.log(this.state.value); // 0
this.setState({ value: this.state.value + 1 });
console.log(this.state.value); // 1
this.setState({ value: this.state.value + 1 });
console.log(this.state.value); // 2

但是,假設(shè)需要提升此狀態(tài)以在幾個(gè)組件之間共享,因此將其移動(dòng)到父級(jí),也就是說(shuō)有props參與到了傳值,那么同步setState模式就會(huì)有問(wèn)題,此時(shí)將state提升到了父組件,利用props將值傳導(dǎo)子組件。

console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0

這是因?yàn)樵谶@個(gè)解決方案中,this.state會(huì)立即刷新,而this.props不會(huì),而且我們不能在不重新渲染父對(duì)象的情況下立即刷新this.props,這意味著我們將不得不放棄批處理的策略。還有更微妙的情況說(shuō)明這如何破壞一致性的,例如這種方案正在混合來(lái)自props尚未刷新和state建議立即刷新的數(shù)據(jù)以創(chuàng)建新狀態(tài)。在React中,this.statethis.props都只在協(xié)調(diào)和刷新之后更新,所以你會(huì)在refactoring之前和之后看到0被打印出來(lái)。這使得提升狀態(tài)安全。在某些情況下這可能會(huì)帶來(lái)不便,特別是對(duì)于來(lái)自更多OO背景的人來(lái)說(shuō),他們只想多次改變狀態(tài),而不是考慮如何在一個(gè)地方表示完整的狀態(tài)更新,我可以理解這一點(diǎn),盡管我確實(shí)認(rèn)為從調(diào)試的角度來(lái)看,保持狀態(tài)更新的集中更加清晰??偠灾?code>React模型并不總是會(huì)產(chǎn)生最簡(jiǎn)潔的代碼,但它在內(nèi)部是一致的,并確保提升狀態(tài)是安全的。

啟用并發(fā)更新

從概念上講React的行為就好像每個(gè)組件都有一個(gè)更新隊(duì)列,我們?cè)谶@里討論是否同步刷新state有一個(gè)前提那就是我們默認(rèn)更新節(jié)點(diǎn)是遵循特定的順序的,但是按默認(rèn)順序更新組件在以后的react中可能就變了。對(duì)于現(xiàn)在我們一直在談?wù)摰漠惒戒秩?,我承認(rèn)我們?cè)趥鬟_(dá)這意味著什么方面做得不是很好,但這就是研發(fā)的本質(zhì):你追求一個(gè)在概念上看起來(lái)很有前途的想法,但只有在花了足夠的時(shí)間之后才能真正理解它的含義。
對(duì)于這個(gè)理由,是React發(fā)展的一個(gè)方向。我們一直在解釋異步渲染的一種方式是React可以根據(jù)setState()調(diào)用的來(lái)源分配不同的優(yōu)先級(jí):事件處理程序、網(wǎng)絡(luò)響應(yīng)、動(dòng)畫等。例如你現(xiàn)在正在打字,那么TextBox組件需要實(shí)時(shí)的刷新,但是當(dāng)你在輸入的時(shí)候,來(lái)了一個(gè)信息,這個(gè)時(shí)候可能讓信息的渲染延遲到某個(gè)閾值,而不是因?yàn)樽枞€程而讓輸入卡頓。如果我們讓某些更新具有較低優(yōu)先級(jí),我們可以將它們的渲染分成幾毫秒的小塊,這樣用戶就不會(huì)注意到它們。異步rendering不僅僅是性能上的優(yōu)化,我們認(rèn)為這是React組件模型可以做什么的根本性轉(zhuǎn)變。例如,考慮從一個(gè)屏幕導(dǎo)航到另一個(gè)屏幕的情況,通常會(huì)在渲染新屏幕時(shí)顯示一個(gè)導(dǎo)航器,但是如果導(dǎo)航速度足夠快,閃爍并立即隱藏導(dǎo)航器會(huì)導(dǎo)致用戶體驗(yàn)下降,更糟糕的是如果有多個(gè)級(jí)別的組件具有不同的異步依賴項(xiàng)例如數(shù)據(jù)、代碼、圖像等,您最終會(huì)得到一連串短暫閃爍的導(dǎo)航器。由于所有的DOM重排,這既在視覺上令人不快,又使您的應(yīng)用程序在實(shí)踐中變慢。如果當(dāng)您執(zhí)行一個(gè)簡(jiǎn)單的setState()來(lái)呈現(xiàn)不同的視圖時(shí),我們可以開始在后臺(tái)呈現(xiàn)更新后的視圖。如果您自己不編寫任何協(xié)調(diào)代碼,您可以選擇在更新時(shí)間超過(guò)某個(gè)閾值時(shí)顯示導(dǎo)航器,否則當(dāng)整個(gè)新子樹的異步依賴項(xiàng)是時(shí)讓React執(zhí)行無(wú)縫轉(zhuǎn)換使?jié)M意。請(qǐng)注意,這只是可能的,因?yàn)?code>this.state不會(huì)立即刷新,如果它被立即刷新,我們將無(wú)法開始在后臺(tái)渲染視圖的新版本,而舊版本仍然可見且可交互,他們獨(dú)立的狀態(tài)更新會(huì)發(fā)生沖突。

參考

https://www.jianshu.com/p/cc12e9a8052c
https://juejin.cn/post/6844903636749778958
https://zh-hans.reactjs.org/docs/faq-state.html
https://blog.csdn.net/zz_jesse/article/details/118282921
https://blog.csdn.net/weixin_44874595/article/details/104270057

到此這篇關(guān)于React中setState同步異步場(chǎng)景的使用的文章就介紹到這了,更多相關(guān)React setState同步異步內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論