手寫一個React-Redux,玩轉React的Context API

上一篇文章咱們手寫了一個Redux,可是單純的Redux只是一個狀態機,是沒有UI呈現的,因此通常咱們使用的時候都會配合一個UI庫,好比在React中使用Redux就會用到React-Redux這個庫。這個庫的做用是將Redux的狀態機和React的UI呈現綁定在一塊兒,當你dispatch action改變state的時候,會自動更新頁面。本文仍是從它的基本使用入手來本身寫一個React-Redux,而後替換官方的NPM庫,並保持功能一致。javascript

本文所有代碼已經上傳GitHub,你們能夠拿下來玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-redux前端

基本用法

下面這個簡單的例子是一個計數器,跑起來效果以下:java

Jul-02-2020 16-44-04

要實現這個功能,首先咱們要在項目裏面添加react-redux庫,而後用它提供的Provider包裹整個ReactApp的根組件:react

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import store from './store'
import App from './App';

ReactDOM.render(
  <react.strictmode>
    <provider store="{store}">
      <app />
    </provider>
  </react.strictmode>,
  document.getElementById('root')
);

上面代碼能夠看到咱們還給Provider提供了一個參數store,這個參數就是Redux的createStore生成的store,咱們須要調一下這個方法,而後將返回的store傳進去:git

import { createStore } from 'redux';
import reducer from './reducer';

let store = createStore(reducer);

export default store;

上面代碼中createStore的參數是一個reducer,因此咱們還要寫個reducer:github

const initState = {
  count: 0
};

function reducer(state = initState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {...state, count: state.count + 1};
    case 'DECREMENT':
      return {...state, count: state.count - 1};
    case 'RESET':
      return {...state, count: 0};
    default:
      return state;
  }
}

export default reducer;

這裏的reduce會有一個初始state,裏面的count0,同時他還能處理三個action,這三個action對應的是UI上的三個按鈕,能夠對state裏面的計數進行加減和重置。到這裏其實咱們React-Redux的接入和Redux數據的組織其實已經完成了,後面若是要用Redux裏面的數據的話,只須要用connectAPI將對應的state和方法鏈接到組件裏面就好了,好比咱們的計數器組件須要count這個狀態和加一,減一,重置這三個action,咱們用connect將它鏈接進去就是這樣:json

import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement, reset } from './actions';

function Counter(props) {
  const { 
    count,
    incrementHandler,
    decrementHandler,
    resetHandler
   } = props;

  return (
    &lt;&gt;
      <h3>Count: {count}</h3>
      <button onclick="{incrementHandler}">計數+1</button>
      <button onclick="{decrementHandler}">計數-1</button>
      <button onclick="{resetHandler}">重置</button>
    
  );
}

const mapStateToProps = (state) =&gt; {
  return {
    count: state.count
  }
}

const mapDispatchToProps = (dispatch) =&gt; {
  return {
    incrementHandler: () =&gt; dispatch(increment()),
    decrementHandler: () =&gt; dispatch(decrement()),
    resetHandler: () =&gt; dispatch(reset()),
  }
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

上面代碼能夠看到connect是一個高階函數,他的第一階會接收mapStateToPropsmapDispatchToProps兩個參數,這兩個參數都是函數。mapStateToProps能夠自定義須要將哪些state鏈接到當前組件,這些自定義的state能夠在組件裏面經過props拿到。mapDispatchToProps方法會傳入dispatch函數,咱們能夠自定義一些方法,這些方法能夠調用dispatchdispatch action,從而觸發state的更新,這些自定義的方法也能夠經過組件的props拿到,connect的第二階接收的參數是一個組件,咱們能夠猜想這個函數的做用就是將前面自定義的state和方法注入到這個組件裏面,同時要返回一個新的組件給外部調用,因此connect其實也是一個高階組件。redux

到這裏咱們彙總來看下咱們都用到了哪些API,這些API就是咱們後面要手寫的目標:api

> Provider: 用來包裹根組件的組件,做用是注入Reduxstore。 > > createStore: Redux用來建立store的核心方法,咱們另外一篇文章已經手寫過了。 > > connect:用來將statedispatch注入給須要的組件,返回一個新組件,他實際上是個高階組件。數組

因此React-Redux核心其實就兩個API,並且兩個都是組件,做用還很相似,都是往組件裏面注入參數,Provider是往根組件注入storeconnect是往須要的組件注入statedispatch

在手寫以前咱們先來思考下,爲何React-Redux要設計這兩個API,假如沒有這兩個API,只用Redux能夠嗎?固然是能夠的!其實咱們用Redux的目的不就是但願用它將整個應用的狀態都保存下來,每次操做只用dispatch action去更新狀態,而後UI就自動更新了嗎?那我從根組件開始,每一級都把store傳下去不就好了嗎?每一個子組件須要讀取狀態的時候,直接用store.getState()就好了,更新狀態的時候就store.dispatch,這樣其實也能達到目的。可是,若是這樣寫,子組件若是嵌套層數不少,每一級都須要手動傳入store,比較醜陋,開發也比較繁瑣,並且若是某個新同窗忘了傳store,那後面就是一連串的錯誤了。因此最好有個東西可以將store全局的注入組件樹,而不須要一層層做爲props傳遞,這個東西就是Provider!並且若是每一個組件都獨立依賴Redux會破壞React的數據流向,這個咱們後面會講到。

React的Context API

React其實提供了一個全局注入變量的API,這就是context api。假如我如今有一個需求是要給咱們全部組件傳一個文字顏色的配置,咱們的顏色配置在最頂級的組件上,當這個顏色改變的時候,下面全部組件都要自動應用這個顏色。那咱們可使用context api注入這個配置:

先使用React.createContext建立一個context

// 咱們使用一個單獨的文件來調用createContext
// 由於這個返回值會被Provider和Consumer在不一樣的地方引用
import React from 'react';

const TestContext = React.createContext();

export default TestContext;

使用Context.Provider包裹根組件

建立好了context,若是咱們要傳遞變量給某些組件,咱們須要在他們的根組件上加上TestContext.Provider,而後將變量做爲value參數傳給TestContext.Provider:

import TestContext from './TestContext';

const setting = {
  color: '#d89151'
}

ReactDOM.render(
  <testcontext.provider value="{setting}">
  	<app />
  </testcontext.provider>,
  document.getElementById('root')
);

使用Context.Consumer接收參數

上面咱們使用Context.Provider將參數傳遞進去了,這樣被Context.Provider包裹的全部子組件均可以拿到這個變量,只是拿這個變量的時候須要使用Context.Consumer包裹,好比咱們前面的Counter組件就能夠拿到這個顏色了,只須要將它返回的JSXContext.Consumer包裹一下就行:

// 注意要引入同一個Context
import TestContext from './TestContext';

// ... 中間省略n行代碼 ...
// 返回的JSX用Context.Consumer包裹起來
// 注意Context.Consumer裏面是一個方法,這個方法就能夠訪問到context參數
// 這裏的context也就是前面Provider傳進來的setting,咱們能夠拿到上面的color變量
return (
    <testcontext.consumer>
      {context =&gt; 
        &lt;&gt;
          <h3 style="{{color:context.color}}">Count: {count}</h3>
          <button onclick="{incrementHandler}">計數+1</button>&nbsp;&nbsp;
          <button onclick="{decrementHandler}">計數-1</button>&nbsp;&nbsp;
          <button onclick="{resetHandler}">重置</button>
        
      }
    </testcontext.consumer>
  );

上面代碼咱們經過context傳遞了一個全局配置,能夠看到咱們文字顏色已經變了:

image-20200703171322676

使用useContext接收參數

除了上面的Context.Consumer能夠用來接收context參數,新版React還有useContext這個hook能夠接收context參數,使用起來更簡單,好比上面代碼能夠這樣寫:

const context = useContext(TestContext);

return (
    &lt;&gt;
      <h3 style="{{color:context.color}}">Count: {count}</h3>
      <button onclick="{incrementHandler}">計數+1</button>&nbsp;&nbsp;
      <button onclick="{decrementHandler}">計數-1</button>&nbsp;&nbsp;
      <button onclick="{resetHandler}">重置</button>
    
);

因此咱們徹底能夠用context api來傳遞redux store,如今咱們也能夠猜想React-ReduxProvider其實就是包裝了Context.Provider,而傳遞的參數就是redux store,而React-ReduxconnectHOC其實就是包裝的Context.Consumer或者useContext。咱們能夠按照這個思路來本身實現下React-Redux了。

手寫Provider

上面說了Provider用了context api,因此咱們要先建一個context文件,導出須要用的context

// Context.js
import React from 'react';

const ReactReduxContext = React.createContext();

export default ReactReduxContext;

這個文件很簡單,新建一個context再導出就好了,對應的源碼看這裏

而後將這個context應用到咱們的Provider組件裏面:

import React from 'react';
import ReactReduxContext from './Context';

function Provider(props) {
  const {store, children} = props;

  // 這是要傳遞的context
  const contextValue = { store };

  // 返回ReactReduxContext包裹的組件,傳入contextValue
  // 裏面的內容就直接是children,咱們不動他
  return (
    <reactreduxcontext.provider value="{contextValue}">
      {children}
    </reactreduxcontext.provider>
  )
}

Provider的組件代碼也不難,直接將傳進來的store放到context上,而後直接渲染children就行,對應的源碼看這裏

手寫connect

基本功能

其實connect纔是React-Redux中最難的部分,裏面功能複雜,考慮的因素不少,想要把它搞明白咱們須要一層一層的來看,首先咱們實現一個只具備基本功能的connect

import React, { useContext } from 'react';
import ReactReduxContext from './Context';

// 第一層函數接收mapStateToProps和mapDispatchToProps
function connect(mapStateToProps, mapDispatchToProps) {
  // 第二層函數是個高階組件,裏面獲取context
  // 而後執行mapStateToProps和mapDispatchToProps
  // 再將這個結果組合用戶的參數做爲最終參數渲染WrappedComponent
  // WrappedComponent就是咱們使用connext包裹的本身的組件
  return function connectHOC(WrappedComponent) {

    function ConnectFunction(props) {
      // 複製一份props到wrapperProps
      const { ...wrapperProps } = props;

      // 獲取context的值
      const context = useContext(ReactReduxContext);

      const { store } = context;  // 解構出store
      const state = store.getState();   // 拿到state

      // 執行mapStateToProps和mapDispatchToProps
      const stateProps = mapStateToProps(state);
      const dispatchProps = mapDispatchToProps(store.dispatch);

      // 組裝最終的props
      const actualChildProps = Object.assign({}, stateProps, dispatchProps, wrapperProps);

      // 渲染WrappedComponent
      return <wrappedcomponent {...actualchildprops}></wrappedcomponent>
    }

    return ConnectFunction;
  }
}

export default connect;

觸發更新

用上面的Providerconnect替換官方的react-redux其實已經能夠渲染出頁面了,可是點擊按鈕還不會有反應,由於咱們雖然經過dispatch改變了store中的state,可是這種改變並無觸發咱們組件的更新。以前Redux那篇文章講過,能夠用store.subscribe來監聽state的變化並執行回調,咱們這裏須要註冊的回調是檢查咱們最終給WrappedComponentprops有沒有變化,若是有變化就從新渲染ConnectFunction,因此這裏咱們須要解決兩個問題:

> 1. 當咱們state變化的時候檢查最終給到ConnectFunction的參數有沒有變化 > 2. 若是這個參數有變化,咱們須要從新渲染ConnectFunction

檢查參數變化

要檢查參數的變化,咱們須要知道上次渲染的參數和本地渲染的參數,而後拿過來比一下就知道了。爲了知道上次渲染的參數,咱們能夠直接在ConnectFunction裏面使用useRef將上次渲染的參數記錄下來:

// 記錄上次渲染參數
const lastChildProps = useRef();
useLayoutEffect(() =&gt; {
  lastChildProps.current = actualChildProps;
}, []);

注意lastChildProps.current是在第一次渲染結束後賦值,並且須要使用useLayoutEffect來保證渲染後當即同步執行。

由於咱們檢測參數變化是須要從新計算actualChildProps,計算的邏輯其實都是同樣的,咱們將這塊計算邏輯抽出來,成爲一個單獨的方法childPropsSelector:

function childPropsSelector(store, wrapperProps) {
  const state = store.getState();   // 拿到state

  // 執行mapStateToProps和mapDispatchToProps
  const stateProps = mapStateToProps(state);
  const dispatchProps = mapDispatchToProps(store.dispatch);

  return Object.assign({}, stateProps, dispatchProps, wrapperProps);
}

而後就是註冊store的回調,在裏面來檢測參數是否變了,若是變了就強制更新當前組件,對比兩個對象是否相等,React-Redux裏面是採用的shallowEqual,也就是淺比較,也就是隻對比一層,若是你mapStateToProps返回了好幾層結構,好比這樣:

{
  stateA: {
    value: 1
  }
}

你去改了stateA.value是不會觸發從新渲染的,React-Redux這樣設計我想是出於性能考慮,若是是深比較,好比遞歸去比較,比較浪費性能,並且若是有循環引用還可能形成死循環。採用淺比較就須要用戶遵循這種範式,不要傳入多層結構,這點在官方文檔中也有說明。咱們這裏直接抄一個它的淺比較:

// shallowEqual.js 
function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y
  } else {
    return x !== x &amp;&amp; y !== y
  }
}

export default function shallowEqual(objA, objB) {
  if (is(objA, objB)) return true

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false
  }

  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)

  if (keysA.length !== keysB.length) return false

  for (let i = 0; i &lt; keysA.length; i++) {
    if (
      !Object.prototype.hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false
    }
  }

  return true
}

在回調裏面檢測參數變化:

// 註冊回調
store.subscribe(() =&gt; {
  const newChildProps = childPropsSelector(store, wrapperProps);
  // 若是參數變了,記錄新的值到lastChildProps上
  // 而且強制更新當前組件
  if(!shallowEqual(newChildProps, lastChildProps.current)) {
    lastChildProps.current = newChildProps;

    // 須要一個API來強制更新當前組件
  }
});

強制更新

要強制更新當前組件的方法不止一個,若是你是用的Class組件,你能夠直接this.setState({}),老版的React-Redux就是這麼幹的。可是新版React-Redux用hook重寫了,那咱們能夠用React提供的useReducer或者useStatehook,React-Redux源碼用了useReducer,爲了跟他保持一致,我也使用useReducer:

function storeStateUpdatesReducer(count) {
  return count + 1;
}

// ConnectFunction裏面
function ConnectFunction(props) {
  // ... 前面省略n行代碼 ... 
  
  // 使用useReducer觸發強制更新
  const [
    ,
    forceComponentUpdateDispatch
  ] = useReducer(storeStateUpdatesReducer, 0);
  // 註冊回調
  store.subscribe(() =&gt; {
    const newChildProps = childPropsSelector(store, wrapperProps);
    if(!shallowEqual(newChildProps, lastChildProps.current)) {
      lastChildProps.current = newChildProps;
      forceComponentUpdateDispatch();
    }
  });
  
  // ... 後面省略n行代碼 ...
}

connect這塊代碼主要對應的是源碼中connectAdvanced這個類,基本原理和結構跟咱們這個都是同樣的,只是他寫的更靈活,支持用戶傳入自定義的childPropsSelector和合並stateProps, dispatchProps, wrapperProps的方法。有興趣的朋友能夠去看看他的源碼:https://github.com/reduxjs/react-redux/blob/master/src/components/connectAdvanced.js

到這裏其實已經能夠用咱們本身的React-Redux替換官方的了,計數器的功能也是支持了。可是下面還想講一下React-Redux是怎麼保證組件的更新順序的,由於源碼中不少代碼都是在處理這個。

保證組件更新順序

前面咱們的Counter組件使用connect鏈接了redux store,假如他下面還有個子組件也鏈接到了redux store,咱們就要考慮他們的回調的執行順序的問題了。咱們知道React是單向數據流的,參數都是由父組件傳給子組件的,如今引入了Redux,即便父組件和子組件都引用了同一個變量count,可是子組件徹底能夠不從父組件拿這個參數,而是直接從Redux拿,這樣就打破了React原本的數據流向。在父-&gt;子這種單向數據流中,若是他們的一個公用變量變化了,確定是父組件先更新,而後參數傳給子組件再更新,可是在Redux裏,數據變成了Redux -&gt; 父,Redux -&gt; 子徹底能夠根據Redux的數據進行獨立更新,而不能徹底保證父級先更新,子級再更新的流程。因此React-Redux花了很多功夫來手動保證這個更新順序,React-Redux保證這個更新順序的方案是在redux store外,再單首創建一個監聽者類Subscription

> 1. Subscription負責處理全部的state變化的回調 > 2. 若是當前鏈接redux的組件是第一個鏈接redux的組件,也就是說他是鏈接redux的根組件,他的state回調直接註冊到redux store;同時新建一個Subscription實例subscription經過context傳遞給子級。 > 3. 若是當前鏈接redux的組件不是鏈接redux的根組件,也就是說他上面有組件已經註冊到redux store了,那麼他能夠拿到上面經過context傳下來的subscription,源碼裏面這個變量叫parentSub,那當前組件的更新回調就註冊到parentSub上。同時再新建一個Subscription實例,替代context上的subscription,繼續往下傳,也就是說他的子組件的回調會註冊到當前subscription上。 > 4. 當state變化了,根組件註冊到redux store上的回調會執行更新根組件,同時根組件須要手動執行子組件的回調,子組件回調執行會觸發子組件更新,而後子組件再執行本身subscription上註冊的回調,觸發孫子組件更新,孫子組件再調用註冊到本身subscription上的回調。。。這樣就實現了從根組件開始,一層一層更新子組件的目的,保證了父-&gt;子這樣的更新順序。

Subscription

因此咱們先新建一個Subscription類:

export default class Subscription {
  constructor(store, parentSub) {
    this.store = store
    this.parentSub = parentSub
    this.listeners = [];        // 源碼listeners是用鏈表實現的,我這裏簡單處理,直接數組了

    this.handleChangeWrapper = this.handleChangeWrapper.bind(this)
  }

  // 子組件註冊回調到Subscription上
  addNestedSub(listener) {
    this.listeners.push(listener)
  }

  // 執行子組件的回調
  notifyNestedSubs() {
    const length = this.listeners.length;
    for(let i = 0; i &lt; length; i++) {
      const callback = this.listeners[i];
      callback();
    }
  }

  // 回調函數的包裝
  handleChangeWrapper() {
    if (this.onStateChange) {
      this.onStateChange()
    }
  }

  // 註冊回調的函數
  // 若是parentSub有值,就將回調註冊到parentSub上
  // 若是parentSub沒值,那當前組件就是根組件,回調註冊到redux store上
  trySubscribe() {
      this.parentSub
        ? this.parentSub.addNestedSub(this.handleChangeWrapper)
        : this.store.subscribe(this.handleChangeWrapper)
  }
}

Subscription對應的源碼看這裏

改造Provider

而後在咱們前面本身實現的React-Redux裏面,咱們的根組件始終是Provider,因此Provider須要實例化一個Subscription並放到context上,並且每次state更新的時候須要手動調用子組件回調,代碼改造以下:

import React, { useMemo, useEffect } from 'react';
import ReactReduxContext from './Context';
import Subscription from './Subscription';

function Provider(props) {
  const {store, children} = props;

  // 這是要傳遞的context
  // 裏面放入store和subscription實例
  const contextValue = useMemo(() =&gt; {
    const subscription = new Subscription(store)
    // 註冊回調爲通知子組件,這樣就能夠開始層級通知了
    subscription.onStateChange = subscription.notifyNestedSubs
    return {
      store,
      subscription
    }
  }, [store])

  // 拿到以前的state值
  const previousState = useMemo(() =&gt; store.getState(), [store])

  // 每次contextValue或者previousState變化的時候
  // 用notifyNestedSubs通知子組件
  useEffect(() =&gt; {
    const { subscription } = contextValue;
    subscription.trySubscribe()

    if (previousState !== store.getState()) {
      subscription.notifyNestedSubs()
    }
  }, [contextValue, previousState])

  // 返回ReactReduxContext包裹的組件,傳入contextValue
  // 裏面的內容就直接是children,咱們不動他
  return (
    <reactreduxcontext.provider value="{contextValue}">
      {children}
    </reactreduxcontext.provider>
  )
}

export default Provider;

改造connect

有了Subscription類,connect就不能直接註冊到store了,而是應該註冊到父級subscription上,更新的時候除了更新本身還要通知子組件更新。在渲染包裹的組件時,也不能直接渲染了,而是應該再次使用Context.Provider包裹下,傳入修改過的contextValue,這個contextValue裏面的subscription應該替換爲本身的。改造後代碼以下:

import React, { useContext, useRef, useLayoutEffect, useReducer } from 'react';
import ReactReduxContext from './Context';
import shallowEqual from './shallowEqual';
import Subscription from './Subscription';

function storeStateUpdatesReducer(count) {
  return count + 1;
}

function connect(
  mapStateToProps = () =&gt; {}, 
  mapDispatchToProps = () =&gt; {}
  ) {
  function childPropsSelector(store, wrapperProps) {
    const state = store.getState();   // 拿到state

    // 執行mapStateToProps和mapDispatchToProps
    const stateProps = mapStateToProps(state);
    const dispatchProps = mapDispatchToProps(store.dispatch);

    return Object.assign({}, stateProps, dispatchProps, wrapperProps);
  }

  return function connectHOC(WrappedComponent) {
    function ConnectFunction(props) {
      const { ...wrapperProps } = props;

      const contextValue = useContext(ReactReduxContext);

      const { store, subscription: parentSub } = contextValue;  // 解構出store和parentSub
      
      const actualChildProps = childPropsSelector(store, wrapperProps);

      const lastChildProps = useRef();
      useLayoutEffect(() =&gt; {
        lastChildProps.current = actualChildProps;
      }, [actualChildProps]);

      const [
        ,
        forceComponentUpdateDispatch
      ] = useReducer(storeStateUpdatesReducer, 0)

      // 新建一個subscription實例
      const subscription = new Subscription(store, parentSub);

      // state回調抽出來成爲一個方法
      const checkForUpdates = () =&gt; {
        const newChildProps = childPropsSelector(store, wrapperProps);
        // 若是參數變了,記錄新的值到lastChildProps上
        // 而且強制更新當前組件
        if(!shallowEqual(newChildProps, lastChildProps.current)) {
          lastChildProps.current = newChildProps;

          // 須要一個API來強制更新當前組件
          forceComponentUpdateDispatch();

          // 而後通知子級更新
          subscription.notifyNestedSubs();
        }
      };

      // 使用subscription註冊回調
      subscription.onStateChange = checkForUpdates;
      subscription.trySubscribe();

      // 修改傳給子級的context
      // 將subscription替換爲本身的
      const overriddenContextValue = {
        ...contextValue,
        subscription
      }

      // 渲染WrappedComponent
      // 再次使用ReactReduxContext包裹,傳入修改過的context
      return (
        <reactreduxcontext.provider value="{overriddenContextValue}">
          <wrappedcomponent {...actualChildProps} />
        </reactreduxcontext.provider>
      )
    }

    return ConnectFunction;
  }
}

export default connect;

到這裏咱們的React-Redux就完成了,跑起來的效果跟官方的效果同樣,完整代碼已經上傳GitHub了:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-redux

下面咱們再來總結下React-Redux的核心原理。

總結

  1. React-Redux是鏈接ReactRedux的庫,同時使用了ReactRedux的API。
  2. React-Redux主要是使用了Reactcontext api來傳遞Reduxstore
  3. Provider的做用是接收Redux store並將它放到context上傳遞下去。
  4. connect的做用是從Redux store中選取須要的屬性傳遞給包裹的組件。
  5. connect會本身判斷是否須要更新,判斷的依據是須要的state是否已經變化了。
  6. connect在判斷是否變化的時候使用的是淺比較,也就是隻比較一層,因此在mapStateToPropsmapDispatchToProps中不要反回多層嵌套的對象。
  7. 爲了解決父組件和子組件各自獨立依賴Redux,破壞了React父級-&gt;子級的更新流程,React-Redux使用Subscription類本身管理了一套通知流程。
  8. 只有鏈接到Redux最頂級的組件纔會直接註冊到Redux store,其餘子組件都會註冊到最近父組件的subscription實例上。
  9. 通知的時候從根組件開始依次通知本身的子組件,子組件接收到通知的時候,先更新本身再通知本身的子組件。

參考資料

官方文檔:https://react-redux.js.org/

GitHub源碼:https://github.com/reduxjs/react-redux/

文章的最後,感謝你花費寶貴的時間閱讀本文,若是本文給了你一點點幫助或者啓發,請不要吝嗇你的贊和GitHub小星星,你的支持是做者持續創做的動力。

「前端進階知識」系列文章及示例源碼: https://github.com/dennis-jiang/Front-End-Knowledges

歡迎關注個人公衆號進擊的大前端第一時間獲取高質量原創~

QR1270

相關文章
相關標籤/搜索