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
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言