本文章內容涵蓋以下
useReducer與useState有相似之處,允許客製化state的邏輯部份。如果想要管理複雜的狀態邏輯,useReducer可以幫助你達到關注點分離(separate the concerns),將其分成渲染和狀態管理,換句話說,也就是在component檔案僅負責渲染UI和派發事件,而複雜的邏輯計算管理狀態的部分可以再抽離出成另個檔案。
從原生的Javascript的Array method就有reduce的方法,如果將reduce輸入到字典翻譯的話,reducer有把...歸納、把...折合(成較小單位),在數學的用詞解釋有簡化、約簡
,所以再回頭看Javascript的Array method也是將整個陣列經過reducer函式後回傳成單一值。
我們再來看React的useReducer就可以嘗試著解釋成將各種複雜的邏輯歸納。
整個流程會如下
這裡將分成以下部分講解
useReducer主要接受兩個參數
如果嘗試著印出useReducer()的話,最後useReducer會回傳一個陣列
陣列的索引值0是state,索引值1是dispatch
console.log(useReducer());
這裡由於未帶入初始值,因此索引值0是undefined
這邊要作為useReducer的參數reducer函式宣告方式也需要兩個參數。
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來將行為分門別類,當收到的action是decrement的時候,將做某事(此範例是將原先數字加一),同理action接收到increment將數字減一。
另外這邊的default意思是當不是上述參數的時候丟出一個例外狀況。
有了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的時候夾帶參數,記得在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>
</>
);
}
這部分要講解的內容與先前文章從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)
希望以上內容有幫助到大家,也歡迎留言如果有錯誤也歡迎糾正。
An Easy Guide to React useReducer() Hook
React useReducer Hook ultimate guide
官方useReducer介紹
reactBeta—Avoiding recreating the initial state