成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

React Context的核心實現,就五行代碼

開發 前端
React Context的實現就是個典型例子,當剔除無關功能的干擾后,他的核心實現,僅需「5行代碼」。本文就讓我們看看React Context的核心實現。

大家好,我卡頌。

很多項目的源碼非常復雜,讓人望而卻步。但在打退堂鼓前,我們應該思考一個問題:源碼為什么復雜?

造成源碼復雜的原因不外乎有三個:

  1. 功能本身復雜,造成代碼復雜。
  2. 編寫者功力不行,寫的代碼復雜。
  3. 功能本身不復雜,但同一個模塊耦合了太多功能,看起來復雜。

如果是原因3,那實際理解起來其實并不難。我們需要的只是有人能幫我們剔除無關功能的干擾。

React Context的實現就是個典型例子,當剔除無關功能的干擾后,他的核心實現,僅需「5行代碼」。

本文就讓我們看看React Context的核心實現。

簡化模型

Context的完整工作流程包括3步:

  1. 定義context
  2. 賦值context
  3. 消費context

以下面的代碼舉例:

const ctx = createContext(null);

function App() {
 return (
  <ctx.Provider value={1}>
   <Cpn />
  </ctx.Provider>
 );
}

function Cpn() {
 const num = useContext(ctx);
 return <div>{num}</div>;
}

其中:

  • const ctx = createContext(null) 用于定義。
  • <ctx.Provider value={1}> 用于賦值。
  • const num = useContext(ctx) 用于消費。

Context數據結構(即createContext方法的返回值)也很簡單:

function createContext(defaultValue) {
  const context = {
    $$typeof: REACT_CONTEXT_TYPE,
    Provider: null,
    _currentValue: defaultValue
  };

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

其中context._currentValue保存context當前值。

context工作流程的三個步驟其實可以概括為:

  1. 實例化context,并將默認值defaultValue賦值給context._currentValue。
  2. 每遇到一個同類型context.Provier,將value賦值給context._currentValue。
  3. useContext(context)就是簡單的取context._currentValue的值就行。

了解了工作流程后我們會發現,Context的核心實現其實就是步驟2。

核心實現

核心實現需要考慮什么呢?還是以上面的示例為例,當前只有一層<ctx.Provider>包裹<Cpn />:

function App() {
 return (
  <ctx.Provider value={1}>
   <Cpn />
  </ctx.Provider>
 );
}

在實際項目中,消費ctx的組件(示例中的<Cpn/>)可能被多級<ctx.Provider>包裹,比如:

const ctx = createContext(0);

function App() {
 return (
    <ctx.Provider value={1}>
      <ctx.Provider value={2}>
        <ctx.Provider value={3}>
          <Cpn />
        </ctx.Provider>
        <Cpn />
      </ctx.Provider>
      <Cpn />
    </ctx.Provider>
  );
}

在上面代碼中,ctx的值會從0(默認值)逐級變為3,再從3逐級變為0,所以沿途消費ctx的<Cpn />組件取得的值分別為:3、2、1。

整個流程就像「操作一個棧」,1、2、3分別入棧,3、2、1分別出棧,過程中棧頂的值就是context當前的值。

基于此,context的核心邏輯包括兩個函數:

function pushProvider(context, newValue) {
 // ...
}

function popProvider(context) {
 // ...
}

其中:

  • 進入ctx.Provider時,執行pushProvider方法,類比入棧操作。
  • 離開ctx.Provider時,執行popProvider方法,類比出棧操作。

每次執行pushProvider時將context._currentValue更新為當前值:

function pushProvider(context, newValue) {
 context._currentValue = newValue;
}

同理,popProvider執行時將context._currentValue更新為上一個context._currentValue:

function popProvider(context) {
 context._currentValue = /* 上一個context value */
}

該如何表示上一個值呢?我們可以增加一個全局變量prevContextValue,用于保存「上一個同類型的context._currentValue」:

let prevContextValue = null;

function pushProvider(context, newValue) {
 // 保存上一個同類型context value
  prevContextValue = context._currentValue;
  context._currentValue = newValue;
}

function popProvider(context) {
  context._currentValue = prevContextValue;
}

在pushProvider中,執行如下語句前:

context._currentValue = newValue;

context._currentValue中保存的就是「上一個同類型的context._currentValue」,將其賦值給prevContextValue。

以下面代碼舉例:

const ctx = createContext(0);

function App() {
 return (
  <ctx.Provider value={1}>
   <Cpn />
  </ctx.Provider>
 );
}

進入ctx.Provider時:

  • prevContextValue賦值為0(context實例化時傳遞的默認值)。
  • context._currentValue賦值為1(當前值)。

當<Cpn />消費ctx時,取得的值就是1。

離開ctx.Provider時:

  • context._currentValue賦值為0(prevContextValue對應值)。

但是,我們當前的實現只能應對一層ctx.Provider,如果是多層ctx.Provider嵌套,我們不知道沿途ctx.Provider對應的prevContextValue。

所以,我們可以增加一個棧,用于保存沿途所有ctx.Provider對應的prevContextValue:

const prevContextValueStack = [];
let prevContextValue = null;

function pushProvider(context, newValue) {
 prevContextValueStack.push(prevContextValue);
  
 prevContextValue = context._currentValue;
 context._currentValue = newValue;
}

function popProvider(context) {
 context._currentValue = prevContextValue;
 prevContextValue = prevContextValueStack.pop();
}

其中:

  • 執行pushProvider時,讓prevContextValue入棧。
  • 執行popProvider時,讓prevContextValue出棧。

至此,完成了React Context的核心邏輯,其中pushProvider三行代碼,popProvider兩行代碼。

兩個有意思的點

關于Context的實現,有兩個有意思的點。

第一個點:這個實現太過簡潔(核心就5行代碼),以至于讓人嚴重懷疑是不是有bug?

比如,全局變量prevContextValue用于保存「上一個同類型的context._currentValue」,如果我們把不同context嵌套使用時會不會有問題?

在下面代碼中,ctxA與ctxB嵌套出現:

const ctxA = createContext('default A');
const ctxB = createContext('default B');

function App() {
  return (
    <ctxA.Provider value={'A0'}>
      <ctxB.Provider value={'B0'}>
        <ctxA.Provider value={'A1'}>
          <Cpn />
        </ctxA.Provider>
      </ctxB.Provider>
      <Cpn />
    </ctxA.Provider>
  );
}

當離開最內層ctxA.Provider時,ctxA._currentValue應該從'A1'變為'A0'。考慮到prevContextValue變量的唯一性以及棧的特性,ctxA._currentValue會不會錯誤的變為'B0'?

答案是:不會。

JSX結構的確定意味著以下兩點是確定的:

  1. ctx.Provider的進入與離開順序。
  2. 多個ctx.Provider之間嵌套的順序。

第一點保證了當進入與離開同一個ctx.Provider時,prevContextValue的值始終與該ctx相關。

第二點保證了不同ctx.Provider的prevContextValue被以正確的順序入棧、出棧。

第二個有意思的點:我們知道,Hook的使用有個限制 —— 不能在條件語句中使用hook。

究其原因,對于同一個函數組件,Hook的數據保存在一條鏈表上,所以必須保證遍歷鏈表時,鏈表數據與Hook一一對應。

但我們發現,useContext獲取的其實并不是鏈表數據,而是ctx._currentValue,這意味著useContext其實是不受這個限制影響的。

總結

以上五行代碼便是React Context的核心實現。在實際的React源碼中,Context相關代碼遠不止五行,這是因為他與其他特性耦合在一塊,比如:

  • 性能優化相關代碼
  • SSR相關代碼

所以,當我們面對復雜代碼時,不要輕言放棄。仔細分析下,沒準兒核心代碼只有幾行呢?

責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2022-04-15 08:07:21

ReactDiff算法

2021-12-16 06:21:16

React組件前端

2016-09-26 17:06:29

2022-05-11 07:41:31

Python驗證碼

2022-02-08 12:30:30

React事件系統React事件系統

2024-12-13 13:58:53

2025-05-09 02:00:00

代碼接口吞吐量

2010-04-19 11:21:32

天御五行可信行為管理

2019-09-04 15:07:15

代碼開發開源

2014-03-26 09:04:42

算法Floyd最短算法

2020-12-20 10:02:17

ContextReactrender

2023-11-01 11:52:22

lunar語言版本

2023-06-16 09:08:39

ReactContextRFC

2022-08-14 23:04:54

React前端框架

2017-03-28 21:03:35

代碼React.js

2019-11-15 15:50:41

JS代碼React前端

2024-08-01 08:45:17

2010-04-19 10:56:30

天御五行安全服務中國電信

2017-01-03 15:38:08

Android

2021-12-26 12:10:21

React組件前端
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲九九 | 成人亚洲视频 | av在线播放网址 | 久久一久久 | 日韩影院一区 | 国产欧美精品一区二区色综合朱莉 | 国产欧美一区二区三区在线看蜜臀 | 日韩国产欧美在线观看 | 久久精品小视频 | 国产精品美女久久久久 | 99精品在线观看 | 亚洲国产精品一区二区www | 国产91久久久久 | 午夜极品 | 国产精品自产拍 | 成人伊人 | 伊人超碰 | 国产日韩精品视频 | 国产精品久久精品 | 国产综合精品一区二区三区 | 国产不卡一区 | 久久精品99 | 国产一区二区三区四区五区加勒比 | 久久久久国产一区二区三区 | а√中文在线8 | 久久中文字幕电影 | 免费a级毛片在线播放 | 成年人视频在线免费观看 | 黄色一级大片在线免费看产 | 激情五月激情综合网 | 国外激情av | 91麻豆精品一区二区三区 | 久久国色| 天天干天天操天天射 | 三级黄视频在线观看 | 久久视频免费观看 | 国产精品亚洲一区 | 久久亚洲国产 | 欧美a在线 | 7777精品伊人久久精品影视 | 久久久区 |