亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文帶你深入理解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)不止這么簡(jiǎ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í)非常簡(jiǎn)單,創(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 中使用 substring() 方法

    如何在 React 中使用 substring() 方法

    這篇文章主要介紹了在 React 中使用 substring() 方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • React生命周期函數(shù)圖解介紹

    React生命周期函數(shù)圖解介紹

    生命周期函數(shù)指在某一時(shí)刻組件會(huì)自動(dòng)調(diào)用并執(zhí)行的函數(shù)。React每個(gè)類組件都包含生命周期方法,以便于在運(yùn)行過程中特定的階段執(zhí)行這些方法
    2022-11-11
  • 面試官常問React的生命周期問題

    面試官常問React的生命周期問題

    在react面試中,面試官經(jīng)常會(huì)問我們一些關(guān)于react的生命周期問題,今天特此分享本文給大家詳細(xì)介紹下,感興趣的朋友跟隨小編一起看看吧
    2021-08-08
  • 關(guān)于react中useCallback的用法

    關(guān)于react中useCallback的用法

    這篇文章主要介紹了關(guān)于react中useCallback的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 詳解React中Props的淺對(duì)比

    詳解React中Props的淺對(duì)比

    這篇文章主要介紹了React中Props的淺對(duì)比的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-05-05
  • React創(chuàng)建對(duì)話框組件的方法實(shí)例

    React創(chuàng)建對(duì)話框組件的方法實(shí)例

    在項(xiàng)目開發(fā)過程中,對(duì)于復(fù)雜的業(yè)務(wù)選擇功能很常見,下面這篇文章主要給大家介紹了關(guān)于React創(chuàng)建對(duì)話框組件的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • 詳解React Angular Vue三大前端技術(shù)

    詳解React Angular Vue三大前端技術(shù)

    當(dāng)前世界中,技術(shù)發(fā)展非常迅速并且變化迅速,開發(fā)者需要更多的開發(fā)工具來解決不同的問題。本文就對(duì)于當(dāng)下主流的前端開發(fā)技術(shù)React、Vue、Angular這三個(gè)框架做個(gè)相對(duì)詳盡的探究,目的是為了解開這些前端技術(shù)的面紗,看看各自的廬山真面目。
    2021-05-05
  • 一文帶你了解React中的函數(shù)組件

    一文帶你了解React中的函數(shù)組件

    函數(shù)式組件的基本意義就是,組件實(shí)際上是一個(gè)函數(shù),不是類,下面這篇文章主要給大家介紹了關(guān)于React中函數(shù)組件的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • react?redux的原理以及基礎(chǔ)使用講解

    react?redux的原理以及基礎(chǔ)使用講解

    這篇文章主要介紹了react?redux的原理以及基礎(chǔ)使用講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • React Native項(xiàng)目框架搭建的一些心得體會(huì)

    React Native項(xiàng)目框架搭建的一些心得體會(huì)

    React Native使你能夠在Javascript和React的基礎(chǔ)上獲得完全一致的開發(fā)體驗(yàn),構(gòu)建世界一流的原生APP。接下來通過本文給大家分享React Native項(xiàng)目框架搭建的一些心得體會(huì),感興趣的朋友跟隨小編一起看看吧
    2021-05-05

最新評(píng)論