iT邦幫忙

2022 iThome 鐵人賽

DAY 18
0
Modern Web

從Create到React—用來實作使用者介面的JavaScript函式庫系列 第 18

為什麼useReducer,reducer詞彙解釋—用流程圖解釋useReducer

  • 分享至 

  • xImage
  •  

本文章內容涵蓋以下

  • 為什麼你需要useReducer
  • 關於reducer詞彙
  • useReducer流程圖
  • useReducer使用方式

為什麼需要useReducer

useReduceruseState有相似之處,允許客製化state的邏輯部份。如果想要管理複雜的狀態邏輯,useReducer可以幫助你達到關注點分離(separate the concerns),將其分成渲染和狀態管理,換句話說,也就是在component檔案僅負責渲染UI派發事件,而複雜的邏輯計算管理狀態的部分可以再抽離出成另個檔案。

關於reducer詞彙

從原生的JavascriptArray method就有reduce的方法,如果將reduce輸入到字典翻譯的話,reducer有把...歸納把...折合(成較小單位),在數學的用詞解釋有簡化、約簡,所以再回頭看JavascriptArray method也是將整個陣列經過reducer函式後回傳成單一值。

useReducer流程圖

我們再來看ReactuseReducer就可以嘗試著解釋成將各種複雜的邏輯歸納。

整個流程會如下

  1. component令事件觸發夾帶Action Object參數呼叫dispatch函式
  2. dispatch函式發送Action Object給Reducer
  3. Reducer處理如何更新state的邏輯後回傳State
  4. State最後使Component重新渲染

useReducer使用方式

這裡將分成以下部分講解

  • useReducer
  • reducer函式
  • dispatch
    • dispatch夾帶payload

useReducer hook

useReducer主要接受兩個參數

  • reducer(簡化器、歸納器):換言之將狀態給歸納整理的一個函式
  • initialState(初始狀態):來自於react的state。

如果嘗試著印出useReducer()的話,最後useReducer會回傳一個陣列
陣列的索引值0是state,索引值1是dispatch

console.log(useReducer());

這裡由於未帶入初始值,因此索引值0是undefined

reducer函式

這邊要作為useReducer的參數reducer函式宣告方式也需要兩個參數。

  • state:來自於react的state
  • action:將不同的行為分門別類

action 通常為一個物件例如

{type:'increment'}

這邊引用官方的範例講解

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

我們要改變計數器的狀態,其中行為分成decrement(增加)increment(減少),因此使用switch case來將行為分門別類,當收到的actiondecrement的時候,將做某事(此範例是將原先數字加一),同理action接收到increment將數字減一。

另外這邊的default意思是當不是上述參數的時候丟出一個例外狀況。

dispatch

有了reducer函式後接下來要處理觸發時機,dispatch解釋成發送,下面範例中我們將dispatch參數帶入一個物件,屬性擁有type,值為decrement和increment做為reducer函式的action的值

function Counter() {
  const initialState = {count: 0};
  const [state, dispatch] = useReducer(reducer, initialState);
  //這邊省略了reducer函式
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

dispatch發送夾帶payload

我們也可以在dispatch的時候夾帶參數,記得在reducer的時候將參數取出作為action行為觸發的時候帶入要處理的邏輯部分。

function Counter() {
    const [number, dispatch] = useReducer(reducer, initialState);
    return (
        <>
            Count: {number.count}
            <button onClick={() => dispatch({ type: 'decrement'})}>-</button>
            <button onClick={() => dispatch({ type: 'increment'})}>+</button>
        </>
    );
}

惰性初始化(Lazy initialization)

這部分要講解的內容與先前文章從jQuery到VirtualDOM來淺談什麼是state—useState完整範例與介紹的useState類似可以進行lazy initialState。

當初始狀態是需要經過複雜的計算的function的時候,可以透過lazy initialzer帶入到useReducer的第三個參數,概念大致如下

function init(initialState){
  // 很多複雜的程式碼
  // 很多複雜的程式碼
  // 很多複雜的程式碼
  //經過複雜的計算後
  return initialState
}

function Counter() {
    const [number, dispatch] = useReducer(reducer, initialState,init);
    return (
        <>
            Count: {number.count}
            <button onClick={() => dispatch({ type: 'decrement'})}>-</button>
            <button onClick={() => dispatch({ type: 'increment'})}>+</button>
        </>
    );
}

透過init這個函式可以讓下次re-render的時候避免再次消耗計算效能,另外如果創建初始狀態的init函式不需要任何引數計算的話,可以將useReducer的第二個參數設為null
如下

const [state, dispatch] = useReducer(reducer, null,init)

希望以上內容有幫助到大家,也歡迎留言如果有錯誤也歡迎糾正/images/emoticon/emoticon41.gif

參考資料

An Easy Guide to React useReducer() Hook
React useReducer Hook ultimate guide
官方useReducer介紹
reactBeta—Avoiding recreating the initial state


上一篇
解決propDrilling問題—簡化consumer的useContext
下一篇
從實作To Do List理解Listing State Up(附圖)(上)
系列文
從Create到React—用來實作使用者介面的JavaScript函式庫30
.

尚未有邦友留言

立即登入留言