城市碼農

理解 Redux 原始碼 (5):從 HOC 到 Hooks,實作 useSelector 與 useDispatch

前言

在上篇理解 Redux 原始碼 (4):透過 Provider 與 connect 理解 React-Redux 的組合中,能理解 React 與 Redux 如何結合,並知道如何實作簡單的 Providerconnect

隨著 React 與 React-Redux 版本的提升,現在官方更建議使用 Hooks 的方式來使用 React-Redux,而非過去的 HOC(High Order Component) 模式 e.g.connect。這樣的轉換是有好處的,將會在接下來的內文中分享。

而 React-Redux Hooks 中,最常用到的就是 useSelector 以及 useDispatch,當使用兩者時,就無需再使用 connect, mapStateToProps, mapDispatchToProps 等,便能取用 Redux store 的資料與方法。因此本文關於 Hooks 的部分,將聚焦於探討這兩個 Hooks 的應用和原始碼,透過實作簡單的 useSelectoruseDispatch 將更理解 React-Redux 的 Hooks 版本。

期待閱讀完本文後能:

  • 理解為什麼要從 HOC 轉為 Hooks 的寫法,好處為何
  • 理解 useSelectoruseDispatch Hook APIs
  • 能實作簡單版本的 useSelectoruseDispatch

p.s. 隨著 React, React-Redux 版本不同,useSelector / useDispatch 的原始碼會有所不同,本文撰寫的範例不一定是最新版本的寫法,但核心概念差異不大,仍可參考並有所學習。


談談為何要從 HOC 轉為 Hooks

每個技術的轉折都有背後的原因,而這段會探討:「為什麼要將 HOC 轉為 Hooks 寫法」。

首先,快速複習 HOC(High Order Component) 的概念:

「HOC 能接收一個 component 作為參數,並最終返回一個新的 component。這個新的 component 通常會擴充或修改原始 component 的 props 或行為。」

connect function 就是 HOC 的概念實踐,它的第一組參數,可以傳入 mapStateToPropsmapDispatchToProps,而第二組參數可以傳入 React component,藉此讓 React component 獲得從 mapStateToPropsmapDispatchToProps 中選擇的 Redux state/dispatch 資料與方法。

講起來有些繞口,透過程式碼更直觀了解:

/*** Counter.js ***/
import { connect } from 'react-redux';

// 宣告 Counter component,並在最下方,有用 connect 包裹才 export,
// 藉此能從 props 中取到 store 的 state/dispatch
const Counter = ({ 
  title,
  count, 
  increment, 
  decrement,
  submit
}) => {
  return (
    <div>
      <div>{title}</div>
      <div>
        <button onClick={decrement}>-</button>
        <span>{count}</span>
        <button onClick={increment}>+</button>
      </div>
      <button onClick={submit}>Submit</button>
    </div>
  );
}

// 宣告 mapStateToProps,設定 Counter 取用的 store state
const mapStateToProps = (state) => ({
  count: state.count,
});

// 宣告 mapDispatchToProps,設定 Counter 取用 store dispatch actions
const mapDispatchToProps = (dispatch) => ({
  increment: () => dispatch({ type: 'INCREMENT' }),
  decrement: () => dispatch({ type: 'DECREMENT' }),
});

// 用 connect 將 Counter 與 store 連結,
// 藉此從 Counter props 中取用 count 資料 & increment, decrement 方法
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

如果對 connect 不熟又想深入瞭解,可以閱讀本系列上篇文章

關於「換成 Hooks 寫法有什麼好處」這個問題可以反過來討論,也就是「目前 HOC 的寫法有什麼壞處」,而好處通常就是補足壞處的部分。因此先了解 HOC 的問題:

HOC 帶來的問題

1. 資料流模糊 (Obscured data flow)

可以看到 Counter 的 component 有著多個 props,但有辦法直觀地分辨出「這些 props 各自從哪裡來」嗎?哪些是從父層級來的?哪些是從 Redux 來的?

const Counter = ({ 
  title,
  count, 
  increment, 
  decrement,
  submit
}) => {
  ......
}
......

非常困難。

除非閱讀到 mapStateToPropsmapDispatchToProps 內的資料邏輯,才有辦法分辨出 title, submit 是從父層級來的 props; 而 count, increment, decrement 是從 Redux 組合而來的 props。

當 component 資料邏輯更多或更複雜時,這個問題也會被放大,不小心就會弄混 props 的來源,進而導致開發上的問題。

2. props 命名衝突 (Name collisions)

connect HOC 背後的概念是將原始 React component 的 props 與 mapStateToPropsmapDispatchToProps 返回的資料與方法結合,概念示意的程式碼如下:

const connect = (mapStateToProps, mapDispatchToProps) => {
  return function (WrappedComponent) {
    return function ConnectedComponent(ownProps) {
      ......

      const stateProps = mapStateToProps ? 
        mapStateToProps(store.getState(), ownProps) 
        : {}; 
      const dispatchProps = mapDispatchToProps ? 
        mapDispatchToProps(store.dispatch, ownProps) 
        : {};

      ......

      // 最終渲染的元件,是透過所有 props 的組合
      return (
        <WrappedComponent 
          {...ownProps} 
          {...stateProps} 
          {...dispatchProps} 
        />
      )
    }
  }
}

重點是關於最後 WrapperdComponent 結合所有 props 的段落,可以想見,假設 ownProps 中,從父層級有個 name props; 而在 stateProps 中,有從 mapStateToProps 提取 key 為 name 的資料,這兩者的資料命名一樣都是 name,就有命名衝突的發生,進而導致部分資料被覆蓋的問題。

而這層命名衝突隱含在 HOC 實作邏輯中,不一定會在 component 層級被發現,因此不好 debug。

3. 靜態型別檢查困難 (Hard to statically type)

當使用 TypeScript 進行靜態型別檢查時,HOC 會讓靜態型別檢查的複雜度增加,最主要的原因是 props 混雜來自「父層級傳入」以及「Redux 傳入」。因此不能只定義其中之一的型別,必須注意兩者來源的型別都要定義好。

舉個簡單範例,說明漏掉 Type Check 的狀況:

type ReduxState = {
  user: {
    id: number;
    name: string;
    age: number;
  };
};

type UserProfileProps = {
  user: {
    id: number;
    name: string;
  };
};

const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
  return <div>{user.name}</div>;
};

const mapStateToProps = (state: ReduxState) => ({
  age: state.user.age,
});

// 最終 export 出去的 Connected UserProfile user props,包含 `age`
// 因此上方的 `React.FC<UserProfileProps>` 型別有問題
export default connect(mapStateToProps)(UserProfile);

由於 Connected 的元件混雜了不同來源的 props,因此在定義型別時,就要特別注意會否遺漏,造成開發上的負擔。

以上是 3 個常見的 HOC 問題,其實還有其他的問題,像是「多層 HOC 鑲嵌之下的偵錯困難、效能問題」等等。

那「Hooks 有解決上述 HOC 的問題嗎」?

Hooks 帶來的好處

這邊先直接換成 Hooks 寫法,用 useSelectoruseDispatch 改寫上面 Counter 的程式碼,在此先不用深入理解 useSelectoruseDispatch 的用法,只要專注在從 HOC 轉成 Hooks 後的程式碼變化及好處,轉換後的程式碼:

/*** Counter.js ***/
// 引入 useSelector, useDispatch 而非 connect
import { useSelector, useDispatch } from 'react-redux';

const Counter = ({ 
  title, 
  submit 
}) => {
  // 使用 useSelector 取得 Redux store 的 count state
  const count = useSelector(state => state.count);

  // 使用 useDispatch 取得 dispatch 函數,
  // 並定義 action creators: increment, decrement
  const dispatch = useDispatch();
  const increment = () => dispatch({ type: 'INCREMENT' });
  const decrement = () => dispatch({ type: 'DECREMENT' });

  return (
    <div>
      <div>{title}</div>
      <div>
        <button onClick={decrement}>-</button>
        <span>{count}</span>
        <button onClick={increment}>+</button>
      </div>
      <button onClick={submit}>Submit</button>
    </div>
  );
}

export default Counter;

轉換成 Hooks 後有什麼好處:

1.資料來源區分明確

原本「來自父層級的 props」 與「來自 Redux 的 props」會混雜,導致「認知負擔增加」、「props 命名衝突進而互相覆蓋」、「型別混雜不好定義」等問題。

使用 Hooks 後,因為有明確區分兩者,所以獲得改善。現在資料來自父層級或者 Redux 一目瞭然,而且如果有命名衝突會報錯而非互相覆蓋,當然,型別定義也會更加單純。

// 來自父層級的資料
const Counter = ({ 
  title, 
  submit 
}) => {
  // 來自 Redux 的資料
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();
  const increment = () => dispatch({ type: 'INCREMENT' });
  const decrement = () => dispatch({ type: 'DECREMENT' });
  ......
}

2.程式碼更簡潔

因為無須宣告與關注 mapStateToPropsmapDispatchToProps,也不用使用 HOC connect 包裹 React component,進而讓程式碼更加簡潔好讀。

3.減少元件的鑲嵌層級

如果多層的 HOC 結構之下,會導致偵錯困難,像是 connect()()()... 的狀況,就要一層一層去抓問題,然而 Hooks 讓原本多層的鑲嵌扁平化,進而減少偵錯時的困難度。

除此之外,由於 Hooks 就是一種 Function 型態,有著更直覺的 input/output,因此撰寫測試上也相對更容易。

在理解使用 Hooks 的好處後,接著將專注於 useSelectoruseDispatch 兩個重要的 React-Redux Hook APIs。


深入認識 useSelector

useSelector 是 React-Redux 提供的 Hook API,用來取用 Redux store 中的狀態。當取用的狀態有所改變時,也會觸發 component 的 re-render。

useSelector 主要接受兩個參數 selector funtionoptions,透過 selector function 就能取得需要的 Redux state,而透過 options 則能夠調整部分設定,通常只會用到第一個 selector 參數。

// useSelector API
const selectedState = useSelector(selector, options)

// useSelector API type
type RootState = ReturnType<typeof store.getState>
type SelectorFn = <Selected>(state: RootState) => Selected
type EqualityFn = (a: any, b: any) => boolean
type CheckFrequency = 'never' | 'once' | 'always'

interface UseSelectorOptions {
  equalityFn?: EqualityFn
  stabilityCheck?: CheckFrequency
  noopCheck?: CheckFrequency
}

const result: Selected = useSelector(
  selector: SelectorFn,
  options?: EqualityFn | UseSelectorOptions
)
    
// useSelector examples
export const Counter = () => {
  const counter = useSelector((state) => state.counter)
  return <div>{counter}</div>
}

export const TodoListItem = ({ id }) => {
  const customizedEqual = (oldValue, newValue) => shadowEqual(oldValue, newValue)
  const todoItem = useSelector((state) => state.todos[id], customizedEqual)
  return <div>{todoItem.text}</div>
}

這邊需要瞭解一個重要的概念「當取用的狀態有所改變時,也會觸發 component re-render。」,具體來說是:

  1. 首先,Redux 更新狀態:當 dispatch 某個 action,觸發 Readux store 更新機制,更新 store state。

  2. 再者,useSelector 判斷是否要重新渲染:當 Redux state 變化時,useSelector 會比較取用的前後 state 是否有不同,如果有不同,才會強制觸發重新渲染。而比較前後兩者 state 是否有變化,預設是使用用 === 嚴格比較的方式,兩者的 reference 必須完全相同時,才會被認為是相同,不需要重新渲染。另外,可以藉由第二個參數 options 傳入客製化的 equalityFn 藉此調整比較 state 是否有變化的方式。

特別注意的是,第二點提及的「比較新舊 state 是否有變化」的預設方式,新版的useSelector 與過去 connect 的比較方式是不同的:

  • useSelector: 預設用嚴格比較(===),新舊 selectedState 兩者的 reference 不同時,觸發重新渲染。
  • connect: 預設用淺層比較(shallowEqual),新舊 selectedState 兩者的值不同時,觸發重新渲染。

這段主要是來自 Redux 官方文件的描述:

useSelector() only forces a re-render if the selector result appears to be different than the last result. The default comparison is a strict === reference comparison. This is different than connect(), which uses shallow equality checks on the results of mapState calls to determine if re-rendering is needed.

雖然當開發者將程式碼從 connect HOC 的方式,改寫成 useSelector 的方式時,在大部分的情況下,並不會因為這個差異而造成問題,然而依然需要了解這個差異,以確保正確使用,更能在發生問題時,更快思考到可能的原因。


實作簡易版的 useSelector

在理解 useSelector API 後,接著來透過實作簡單版本的 useSelector,藉此更認識它的底層邏輯。

這邊的實作將會聚焦在 useSelector(selector, equalityFn) 實踐,需要注意:

  1. 傳入的 selector 參數 (reduxState) => reduxState.xxx,能選擇出最終要回傳的 Redux state。
  2. 需要在 Redux store state 有任何改變時,觸發重新渲染 => 意即需訂閱 store ,在 store state 改變時觸發 forceRender。
  3. 更進一步,需要確認 Redux store state 的改變影響到 useSelector 所選的 state 時,才需要重新渲染,避免無謂的渲染 => 意即需要紀錄上次的 state,才能做新舊 state 比較。
  4. 傳入的 eqalityFn 參數,可作為判斷新舊 state 是否改變的函式,若沒有傳入,預設用 === 判斷新舊 state 是否改變。

1.「製作 input / output」的介面

  • input: selector 及 equalityFn
  • output: selectedState

selector function 的形式會是 (reduxState) => reduxState.xxx,而透過 selector(store.getState()) 就能製作出最終回傳的 selectedState

/** useSelector.js file **/
import { useContext } from "react";
// 已製作的 ReduxContext,在此無需關注細節,有興趣瞭解可看上篇內容
import { ReduxContext } from './Provider' 

const useSelector = (
  selector,
  equalityFn
)  => {
  // 從 ReduxContext 中取出 store,可以提供 getState/dispatch 方法
  const store = useContext(ReduxContext); 
    
  // 透過 selector,選出最後要回傳的 Redux State
  const selectedState = selector(store.getState());
    
  // 回傳最後選出的 State
  return selectedState;
}

export default useSelector;

2. 實踐「若 Redux state 有任何改變時,觸發重新渲染」

當 Redux state 改變時,要觸發重新渲染,換句話說就是利用 store.subscribe 訂閱 forceRender,如此,當 store state 改變時,就會執行 forceRender

/** useSelector.js file **/
import { 
    useContext, 
    useReducer, 
    useEffect 
} from "react";
import { ReduxContext } from './Provider' 

const useSelector = (
  selector,
  equalityFn
)  => {
  const store = useContext(ReduxContext); 
    
  // 透過 useReducer,製作用來觸發重新渲染的 forceRender
  const [, forceRender] = useReducer(s => s + 1, 0);
    
  const selectedState = selector(store.getState());

  useEffect(() => {
    // 透過 store.subscribe 訂閱機制
    // dispatch action 到 Redux store 時,呼叫 forceRender
    const unsubscribe = store.subscribe(() => forceRender());
    return unsubscribe;
  }, []);

  return selectedState;
}

export default useSelector;

3. 實踐「若 useSelecter 所選 state 有改變時,才觸發重新渲染」

可以透過 useRef 將前一版本的 selectedState 記錄下來,如此就能比對新舊版本的 state 究竟有沒有改變,如果有改變才需要去觸發 forceRender

/** useSelector.js file **/
import { 
    useContext, 
    useReducer, 
    useRef, 
    useEffect 
} from "react";
import { ReduxContext } from './Provider' 

const useSelector = (
  selector,
  equalityFn
)  => {
  const store = useContext(ReduxContext); 
  const [, forceRender] = useReducer(s => s + 1, 0);

  // 利用 useRef 製作儲存上次的 selectedState
  const prevSelectedState = useRef(null);
  const selectedState = selector(store.getState());

  // 實作 checkForUpdates 來檢查新舊 selectedState 是否有變化
  function checkForUpdates() {
    // 取出最新的 selectedState
    const newSelectedState = selector(store.getState());
    // 若新舊 selectedState 相等,不會重新渲染
    if(newSelectedState === prevSelectedState.current) {
        return;
    }
    // 若新舊 selectedState 不相等,觸發重新渲染
    prevSelectedState.current = newSelectedState;
    forceRender();
  }

  useEffect(() => {
    // dispatch action 到 Redux store 時,呼叫 checkForUpdates
    const unsubscribe = store.subscribe(checkForUpdates);
    return unsubscribe;
  }, []);

  return selectedState;
}

export default useSelector;

4. 實踐「可用傳入的 equalityFn 客製化 function,比對新舊 state 是否有變化」

可以利用傳入的第二個參數 equalityFn 去判斷新舊的 selectedState 有沒有變化。

/** useSelector.js file **/
import { 
    useContext, 
    useReducer, 
    useRef, 
    useEffect 
} from "react";
import { ReduxContext } from './Provider' 

// 預設的 equalityFn,用 strict equal 模式
const defaultEqualityFn = (a, b) => a === b;

const useSelector = (
  selector,
  // 可傳入的客製化的 equalityFn,預設為 defaultEqualityFn
  equalityFn = defaultEqualityFn 
)  => {
  const store = useContext(ReduxContext); 
  const [, forceRender] = useReducer(s => s + 1, 0);

  const prevSelectedState = useRef(null);
  const selectedState = selector(store.getState());

  function checkForUpdates() {
    const newSelectedState = selector(store.getState());
     // 用 equalityFn 來判斷新舊 selectedState 是否相等
     // 若新舊 selectedState 相等,不會重新渲染
    if(equalityFn(newSelectedState, latestSelectedState.current)) {
        return;
    }
    // 若新舊 selectedState 不相等,觸發重新渲染
    prevSelectedState.current = newSelectedState;
    forceRender();
  }

  useEffect(() => {
    // dispatch action 到 Redux store 時,呼叫 checkForUpdates
    const unsubscribe = store.subscribe(checkForUpdates);
    return unsubscribe;
  }, []);

  return selectedState;
}

export default useSelector;

至此,就完成了簡易版 useSelector 的核心邏輯,接著會討論 useDispatch 的部分囉。


深入認識 useDispatch

useDispatch 是 React-Redux 提供的另一個 Hook API,它會回傳 store 的 dispatch 方法,,透過 dispatch 方法就能發送 actions 到 Redux store,藉此更新 Redux store state。

使用 useDispatch 可以讓我們更簡單地在 component 中觸發 Redux actions,而不需要透過 connect HOC 方式或其他複雜的方法。

// useDispatch API
const dispatch = useDispatch()

// useDispatch API type
interface Dispatch<A extends Action = UnknownAction> {
  <T extends A>(action: T, ...extraArgs: any[]): T
}
const dispatch: Dispatch = useDispatch()

// useDispatch example
export const Counter = ({ value }) => {
  const dispatch = useDispatch()
  const incrementCounter = () => dispatch(
    { type: 'INCREAMENT' }
  )

  return (
    <div>
      <span>{value}</span>
      <button onClick={incrementCounter}>
        Increment counter
      </button>
    </div>
  )
}

值得注意的是,只要 Redux store 的 instance 是相同的情況下, dispatch 的 reference 都會是相同的,通常而言,一個 application 只會有一個 Provider 以及 create 一次 store 傳入,所以 dispatch 作為 useEffect or 其他 Hooks 的 dependecy 是很安全的,不會觸發無限循環 loop 等問題。

這段是來自於 Redux 官方文件的描述:

The dispatch function reference will be stable as long as the same store instance is being passed to the <Provider>. Normally, that store instance never changes in an application.

接著,就來實作簡易版的 useDispatch


實作簡易版的 useDispatch

要實作簡易版的 useDispatch,其實非常直觀。只需要從 ReduxContext 中取得 Redux store 的 dispatch function,然後回傳它,就可以達成。

/** useDispatch.js file **/
import { useContext } from "react";
import { ReduxContext } from './Provider';

const useDispatch = () => {
  // 從 ReduxContext 中取得 store
  const store = useContext(ReduxContext);

  // 回傳 store 的 dispatch function
  return store.dispatch;
}

export default useDispatch;

在這個簡易版的 useDispatch 中,先使用了 useContext(ReduxContext) 來從 ReduxContext 中取得 Redux store,接著再直接回傳 store 的 dispatch 函式。

如此一來,當開發者在元件中使用 useDispatch 時,就可以提取 dispatch 並且發送 actions 到 Redux store,而不需要透過 connect 加上 mapDispatchToProps 發送 actions 囉。

而當從 ReduxContext 取得 store 時,這個 store 是來自於 Provider 傳遞的 store,而通常而言,store 在 App 中會是只在最初創建一次,透過 createStore(...) 等方式,所以 storestore.dispatch 都會是 stable,不會隨意改變 reference。

相比於 useSelectoruseDisatch 的實作相對簡單許多。


總結,回顧最初的目標

當閱讀過 HOC 與 Hooks 型態的原始碼時,的確會認為後者相對而言更容易理解,我覺得有個關鍵是在於鑲嵌層級比較沒那麼多,且 Hooks 比起來是更存粹的 Function,也令人更直覺地理解之。

當然,真正的原始碼更加複雜,有興趣的非常推薦直接去閱讀喔,比起 HOC 真的更好理解了。在此,回顧本文最初的目標:

1. 理解為什麼要從 HOC 轉為 Hooks 的寫法,好處為何

從 HOC 轉為 Hooks 主要有幾點好處:

資料來源區分明確

使用 Hooks 後,因為有明確區分「來自父層級的 props 資料方法」 與「來自 Redux 資料方法」,所以能解決「認知負擔增加」、「props 命名衝突進而互相覆蓋」、「型別混雜不好定義」等問題。

程式碼更簡潔

因為無須宣告與關注 mapStateToPropsmapDispatchToProps,也不用使用 HOC connect 包裹 React component,進而讓程式碼更加簡潔好讀。

減少元件的鑲嵌層級

如果多層的 HOC 結構之下,會導致偵錯困難,像是 connect()()()... 的狀況,就要一層一層去抓問題,然而 Hooks 讓原本多層的鑲嵌扁平化,進而減少偵錯時的困難度。

讓測試更好撰寫

比起 HOC 的模式,Hooks 算是更簡單的 Function 結構,因此相對更好撰寫測試。

2. 理解 useSelectoruseDispatch Hook APIs

useSelector API

useSelector 是 React-Redux 提供的 Hook API,用來取用 Redux store 中的狀態。當取用的狀態有所改變時,也會觸發 component 的 re-render。

useSelector 主要接受兩個參數 selector funtionoptions,透過 selector function 就能取得需要的 Redux state,而透過 options 則能夠調整部分設定。

const selectedState = useSelector(selector, options)

useDispatch API

useDispatch 是 React-Redux 提供的另一個 Hook API,它會回傳 store 的 dispatch 方法,,透過 dispatch 方法就能發送 actions 到 Redux store,藉此更新 Redux store state。

const dispatch = useDispatch()

3. 能實作簡單版本的 useSelectoruseDispatch

實作 useSelector

/** useSelector.js file **/
import { 
    useContext, 
    useReducer, 
    useRef, 
    useEffect 
} from "react";
import { ReduxContext } from './Provider' 

// 預設的 equalityFn,用 strict equal 模式
const defaultEqualityFn = (a, b) => a === b;

const useSelector = (
  // 可傳入負責選擇 selectedState 的 selector 函式
  selector,
  // 可傳入的客製的 equalityFn,預設為 defaultEqualityFn
  equalityFn = defaultEqualityFn 
)  => {
  const store = useContext(ReduxContext); 
  // 利用 useReducer 製作 forceRender,藉此在特定時機觸發重新渲染
  const [, forceRender] = useReducer(s => s + 1, 0);

  // 用 useRef 儲存前一次的 selectedState,
  // 藉此用於判斷新舊 selectedState 是否改變
  const prevSelectedState = useRef(null);
  const selectedState = selector(store.getState());

  function checkForUpdates() {
    const newSelectedState = selector(store.getState());
     // 用 equalityFn 來判斷新舊 selectedState 是否相等
     // 若新舊 selectedState 相等,不會重新渲染
    if(equalityFn(newSelectedState, latestSelectedState.current)) {
        return;
    }
    // 若新舊 selectedState 不相等,觸發重新渲染
    prevSelectedState.current = newSelectedState;
    forceRender();
  }

  useEffect(() => {
    // 若 dispatch action 到 Redux store 時,呼叫 checkForUpdates
    const unsubscribe = store.subscribe(checkForUpdates);
    return unsubscribe;
  }, []);

  // 回傳選擇出來的 selectedState
  return selectedState;
}

export default useSelector;

實作 useDispatch

/** useDispatch.js file **/
import { useContext } from "react";
import { ReduxContext } from './Provider';

const useDispatch = () => {
  // 從 ReduxContext 中取得 store
  const store = useContext(ReduxContext);

  // 回傳 store 的 dispatch function
  return store.dispatch;
}

export default useDispatch;

以上就是本文的內容,希望對閱讀的你有幫助,若有興趣也可閱讀下方更多的參考資料囉。


參考資料

如果分享的內容對你有幫助,歡迎 點此小額贊助請我喝杯咖啡 😌 ; 如果發現部落格文章內容有誤,或有想進一步討論的內容,歡迎點此前往開 Issues 討論,感謝!