最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文帶你深入理解React中的Context

 更新時(shí)間:2023年05月22日 10:04:38   作者:Gamble_  
React?Context是React提供給開發(fā)者的一種常用的狀態(tài)管理機(jī)制,本文主要來和大家講講為什么需要Context,又是如何使用Context的,感興趣的可以了解一下

前言

React Context是React提供給開發(fā)者的一種常用的狀態(tài)管理機(jī)制,利用Context可以有效的將同一個(gè)狀態(tài)在多級(jí)組件中進(jìn)行傳遞,并能夠在狀態(tài)更新時(shí),自動(dòng)的通知各個(gè)組件進(jìn)行更新。那React Context又是如何做到這一點(diǎn)的,以及為什么需要這么設(shè)計(jì)呢?

為什么需要Context

在React的數(shù)據(jù)管理理念中,一直遵循著單項(xiàng)數(shù)據(jù)流以及數(shù)據(jù)不變性的理念。當(dāng)我們需要從父組件將狀態(tài)向子組件傳遞時(shí),我們往往需要通過Props顯式進(jìn)行傳遞,例如:

const Father:FC = () => {
  const [count, setCount] = useState<number>(0)
  return (
    <Son count={count} />
  )
}
const Son:FC = (props) => {
  const { count } = props;
  return (
    <span>{count}</span>
  )
}

但是,倘若父組件需要向子組件的子組件,也就是孫組件進(jìn)行狀態(tài)的傳遞呢?或者父組件需要同時(shí)向多個(gè)子組件進(jìn)行傳遞呢?當(dāng)然,繼續(xù)使用props進(jìn)行逐層往下的顯示傳遞肯定也是能實(shí)現(xiàn)這個(gè)需求的,但那樣的代碼未免過于繁瑣且難以維護(hù),如果能夠在父組件里維護(hù)一個(gè)類似于Js里的全局變量,所有的子組件都能使用這個(gè)全局變量不就好了嗎?

是的,這個(gè)就是Context的作用,但又遠(yuǎn)遠(yuǎn)不止這么簡單。

Context是什么

Context提供了一個(gè)無需為每層組件手動(dòng)添加 props,就能在組件樹間進(jìn)行數(shù)據(jù)傳遞的方法。

Context如何使用

創(chuàng)建Context

首先,我們需要在父組件中, 利用React.createContext創(chuàng)建一個(gè)React Context對(duì)象,這個(gè)方法接受一個(gè)入?yún)?,作為?dāng)前Context的默認(rèn)值。

import React from 'react'
const Context = React.createContext(defaultValue)

向下傳遞數(shù)據(jù)

利用Context對(duì)象返回的Provide組件,包裹需要傳遞數(shù)據(jù)的子組件。

每個(gè) Context 對(duì)象都會(huì)返回一個(gè) Provider React 組件,它接收一個(gè) value 屬性,可將數(shù)據(jù)向下傳遞給消費(fèi)組件。當(dāng) Provider 的 value 值發(fā)生變化時(shí),它內(nèi)部的所有消費(fèi)組件都會(huì)重新渲染。

const Father:FC = () => {
  const [count, setCount] = useState<number>(0)
  return (
    <Context.Provider value={count}>
    	<Son />
    </Context.Provider>
  )
}

接收數(shù)據(jù)

被包裹的子組件,利用useContext獲取父組件傳遞的數(shù)據(jù)。

const Son:FC = (props) => {
  const value = React.useContext(Context);
  return (
    <span>{value}</span>
  )
}

Context如何以及為何這樣實(shí)現(xiàn)

讓我們回到Context使用過程的第一步,通過閱讀源碼去研究createContext究竟做了什么樣的工作?

剔除了一些干擾代碼,其實(shí)createContext做的事情其實(shí)非常簡單,創(chuàng)建了一個(gè)對(duì)象,保存了當(dāng)前context的value, 以及返回了一個(gè)Provide組件。

import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
import type {ReactProviderType} from 'shared/ReactTypes';
import type {ReactContext} from 'shared/ReactTypes';
export function createContext<T>(defaultValue: T): ReactContext<T> {
  // TODO: Second argument used to be an optional `calculateChangedBits`
  // function. Warn to reserve for future use?
  const context: ReactContext<T> = {
    $$typeof: REACT_CONTEXT_TYPE,
    // As a workaround to support multiple concurrent renderers, we categorize
    // some renderers as primary and others as secondary. We only expect
    // there to be two concurrent renderers at most: React Native (primary) and
    // Fabric (secondary); React DOM (primary) and React ART (secondary).
    // Secondary renderers store their context values on separate fields.
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    // Used to track how many concurrent renderers this context currently
    // supports within in a single renderer. Such as parallel server rendering.
    _threadCount: 0,
    // These are circular
    Provider: (null: any),
    Consumer: (null: any),
    // Add these to use same hidden class in VM as ServerContext
    _defaultValue: (null: any),
    _globalName: (null: any),
  };
  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };
  return context;
}

在React編譯的過程中,會(huì)將我們寫的JSX語法代碼,轉(zhuǎn)化成React.createElement方法,執(zhí)行這個(gè)方法后,會(huì)得到一個(gè)ReactElement元素對(duì)象,也就是我們所說的Virtual Dom。這個(gè)元素對(duì)象,會(huì)記錄著當(dāng)前組件所接收的入?yún)⒁约霸仡愋汀?/p>

而Provide組件實(shí)際上編譯完之后也是一個(gè)ReactElement,只不過他的Type跟正常的組件并不一樣,而是context.Provider。

  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };

那么,子組件又是如何利用Provider和useContext去獲取到最新的數(shù)據(jù)的呢?

useContext接收一個(gè)context對(duì)象作為參數(shù),從context._currentValue中讀取當(dāng)前contetx的value值。

function readContextForConsumer<T>(
  consumer: Fiber | null,
  context: ReactContext<T>,
): T {
  // 獲取當(dāng)前context保存的value
  const value = isPrimaryRenderer
    ? context._currentValue
    : context._currentValue2;
	// ...do something
  // 返回當(dāng)前的值
  return value;
}

問題又來了,當(dāng)父組件的狀態(tài)改變時(shí),又是如何通過Provider觸發(fā)更新,通知訂閱當(dāng)前狀態(tài)的子組件進(jìn)行重新渲染的呢?

當(dāng)父組件的狀態(tài)進(jìn)行更新時(shí),React整體會(huì)進(jìn)入到調(diào)度更新階段,F(xiàn)iber節(jié)點(diǎn)會(huì)進(jìn)入到beginWork的方法當(dāng)中,在這個(gè)方法里面,會(huì)根據(jù)當(dāng)前更新節(jié)點(diǎn)的類型,從而執(zhí)行相對(duì)應(yīng)的方法。上文提到,Provider組件是有單獨(dú)的自己的類型ContextProvider的,所以會(huì)進(jìn)入到相對(duì)應(yīng)的更新方法,updateContextProvide。

其實(shí)updateContextProvide里做的事情,大抵可以概括為:

首先更新context._currentValue, 然后比較新老value是否發(fā)生改變,如果沒有發(fā)生改變,則跳出更新函數(shù),復(fù)用當(dāng)前fiber節(jié)點(diǎn)。如果發(fā)生了改變,則調(diào)用一個(gè)叫propagateContextChange的方法,對(duì)該P(yáng)rovider組件的子組件進(jìn)行深度遍歷,找到訂閱了當(dāng)前context的子組件,并打上需要更新的標(biāo)記,lane。

function updateContextProvider(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  const providerType: ReactProviderType<any> = workInProgress.type;
  const context: ReactContext<any> = providerType._context;
  const newProps = workInProgress.pendingProps;
  const oldProps = workInProgress.memoizedProps;
  const newValue = newProps.value;
  pushProvider(workInProgress, context, newValue);
  if (enableLazyContextPropagation) {
    // In the lazy propagation implementation, we don't scan for matching
    // consumers until something bails out, because until something bails out
    // we're going to visit those nodes, anyway. The trade-off is that it shifts
    // responsibility to the consumer to track whether something has changed.
  } else {
    if (oldProps !== null) {
      const oldValue = oldProps.value;
      if (is(oldValue, newValue)) {
        // No change. Bailout early if children are the same.
        if (
          oldProps.children === newProps.children &&
          !hasLegacyContextChanged()
        ) {
          return bailoutOnAlreadyFinishedWork(
            current,
            workInProgress,
            renderLanes,
          );
        }
      } else {
        // The context value changed. Search for matching consumers and schedule
        // them to update.
        propagateContextChange(workInProgress, context, renderLanes);
      }
    }
  }
	// do something...
}

那么, 在深度遍歷的時(shí)候,又是如何知道當(dāng)前子組件是否有訂閱當(dāng)前Context的呢?

其實(shí)在使用useContext的時(shí)候,除了讀取當(dāng)前context的value,還會(huì)把接收的context對(duì)象信息保存在當(dāng)前組件的Fiber.dependencies上,所以在遍歷的時(shí)候,只需要看當(dāng)前組件的dependencies上有沒有當(dāng)前context便可以知道當(dāng)前組件是否存在訂閱關(guān)系了。

function readContextForConsumer<T>(
  consumer: Fiber | null,
  context: ReactContext<T>,
): T {
  const value = isPrimaryRenderer
    ? context._currentValue
    : context._currentValue2;
  if (lastFullyObservedContext === context) {
    // Nothing to do. We already observe everything in this context.
  } else {
    const contextItem = {
      context: ((context: any): ReactContext<mixed>),
      memoizedValue: value,
      next: null,
    };
    if (lastContextDependency === null) {
      lastContextDependency = contextItem;
      consumer.dependencies = {
        lanes: NoLanes,
        firstContext: contextItem,
      };
      if (enableLazyContextPropagation) {
        consumer.flags |= NeedsPropagation;
      }
    } else {
      // Append a new context item.
      lastContextDependency = lastContextDependency.next = contextItem;
    }
  }
  return value;
}

只有被Provider組件包裹的子組件才能讀取到Context的狀態(tài)嗎?

其實(shí)并不是,所有的組件都可以通過useContext去讀取Context對(duì)象里的currentValue,但是,只有被Provider組件包裹的組件,才能訂閱到Context對(duì)象里的value的變化,在變化的時(shí)候及時(shí)的更新自身組件的狀態(tài)。這樣設(shè)計(jì)的目的,實(shí)際上也是為了更好的優(yōu)化React在更新組件的性能,試想,如果每創(chuàng)建一個(gè)Context對(duì)象,就默認(rèn)所有的組件都可以訂閱到這個(gè)Context的變化,那么整個(gè)Fiber樹在更新的過程中,需要遍歷的Fiber節(jié)點(diǎn)就太龐大了,一些完全不需要且沒有訂閱當(dāng)前Context的組件也需要被遍歷到,這其實(shí)是一種性能的浪費(fèi)。

到此這篇關(guān)于一文帶你深入理解React中的Context的文章就介紹到這了,更多相關(guān)React Context內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入理解React中何時(shí)使用箭頭函數(shù)

    深入理解React中何時(shí)使用箭頭函數(shù)

    對(duì)于剛學(xué)前端的大家來說,對(duì)于React中的事件監(jiān)聽寫法有所疑問很正常,特別是React中箭頭函數(shù)使用這塊,下面這篇文章主要給大家深入的講解了關(guān)于React中何時(shí)使用箭頭函數(shù)的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • 詳解react native頁面間傳遞數(shù)據(jù)的幾種方式

    詳解react native頁面間傳遞數(shù)據(jù)的幾種方式

    這篇文章主要介紹了詳解react native頁面間傳遞數(shù)據(jù)的幾種方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • 一看就懂的ReactJs基礎(chǔ)入門教程-精華版

    一看就懂的ReactJs基礎(chǔ)入門教程-精華版

    現(xiàn)在最熱門的前端框架有AngularJS、React、Bootstrap等。自從接觸了ReactJS,ReactJs的虛擬DOM(Virtual DOM)和組件化的開發(fā)深深的吸引了我,下面來跟我一起領(lǐng)略ReactJs的風(fēng)采吧~~ 文章有點(diǎn)長,耐心讀完,你會(huì)有很大收獲哦
    2021-04-04
  • react-router?v6實(shí)現(xiàn)權(quán)限管理+自動(dòng)替換頁面標(biāo)題的案例

    react-router?v6實(shí)現(xiàn)權(quán)限管理+自動(dòng)替換頁面標(biāo)題的案例

    這篇文章主要介紹了react-router?v6實(shí)現(xiàn)權(quán)限管理+自動(dòng)替換頁面標(biāo)題,這次項(xiàng)目是有三種權(quán)限,分別是用戶,商家以及管理員,這次寫的權(quán)限管理是高級(jí)權(quán)限能訪問低級(jí)權(quán)限的所有頁面,但是低級(jí)權(quán)限不能訪問高級(jí)權(quán)限的頁面,需要的朋友可以參考下
    2023-05-05
  • Electron整合React使用搭建開發(fā)環(huán)境的步驟詳解

    Electron整合React使用搭建開發(fā)環(huán)境的步驟詳解

    這篇文章主要介紹了Electron整合React使用搭建開發(fā)環(huán)境,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2020-06-06
  • react router4+redux實(shí)現(xiàn)路由權(quán)限控制的方法

    react router4+redux實(shí)現(xiàn)路由權(quán)限控制的方法

    本篇文章主要介紹了react router4+redux實(shí)現(xiàn)路由權(quán)限控制的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • 快速創(chuàng)建React項(xiàng)目并配置webpack

    快速創(chuàng)建React項(xiàng)目并配置webpack

    這篇文章主要介紹了創(chuàng)建React項(xiàng)目并配置webpack,在這里需要注意,Create?React?App?requires?Node?14?or?higher.需要安裝高版本的node,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-01-01
  • React Fiber樹的構(gòu)建和替換過程講解

    React Fiber樹的構(gòu)建和替換過程講解

    React Fiber樹的創(chuàng)建和替換過程運(yùn)用了雙緩存技術(shù),直接將舊的 fiber 樹替換成新的 fiber 樹,這樣做的好處是省去了直接在頁面上渲染時(shí)的計(jì)算時(shí)間,避免計(jì)算量大導(dǎo)致的白屏、卡頓,現(xiàn)在你一定還不太理解,下面進(jìn)行詳細(xì)講解,需要的朋友可以參考下
    2022-12-12
  • React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂播放時(shí)間滾動(dòng))

    React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂播放時(shí)間滾動(dòng))

    這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂播放使勁按滾動(dòng)),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2024-02-02
  • mobx在react hooks中的應(yīng)用方式

    mobx在react hooks中的應(yīng)用方式

    這篇文章主要介紹了mobx在react hooks中的應(yīng)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10

最新評(píng)論

揭阳市| 江口县| 丹阳市| 峡江县| 全南县| 高陵县| 从江县| 五家渠市| 从化市| 阳东县| 望谟县| 临猗县| 浮山县| 电白县| 延寿县| 云南省| 策勒县| 长海县| 会理县| 乌什县| 崇信县| 玉林市| 冷水江市| 会东县| 乌拉特后旗| 乌恰县| 峨眉山市| 紫阳县| 洛川县| 荃湾区| 乐陵市| 兖州市| 宣威市| 连平县| 樟树市| 托里县| 犍为县| 云安县| 江山市| 资中县| 峨山|