業務でuseReducerを初めて使う機会があったので、簡単にまとめておきます。
useReducerとは
useReducerは、リデューサ (reducer) をコンポーネントに追加するための React フックです。
※reducerとは、コンポーネントの外部に、単一の関数にすべての state 更新ロジックを集約した関数のこと。(直訳すると、reduce=減らす)
useStateとの違い
シンプルな文字列や数値、真偽値であれば、useStateが適していますが、オブジェクトや配列、また、変更パターンが複雑な場合はuseReducerが適しています。
https://ja.react.dev/reference/react/useReducer より引用
基本的な使い方
const [state, dispatch] = useReducer(reducer, initialArg, init?)
それぞれの値は、下記の役割となっています。
-
state
: 管理する値 -
dispatch
: reducer を呼び出すための関数 -
reducer
: stateを更新するための関数。引数にstateとactioinをとり、更新されたstateを返す。actionとは何をするかを示すオブジェクト。 -
initialArg
: state初期値 -
init
: 省略可能。state初期値を返す関数。指定されていない場合、初期 state は initialArg そのものになる
例題としてよく使われるカウンターを例にしてみたいと思います。
stateが数値の場合
const initialState = 0;
const [count, dispatch] = useReducer(reducer, initialState)
- dispatch
<Button onClick={()=>dispatch('increment')}>+1</Button>
<Button onClick={()=>dispatch('decrement')}>-1</Button>
- reducer
const reducer = (count, action)=> {
switch (action){
case 'increment':
return count + 1
case 'decrement':
return count - 1
default:
return count
}
}
stateがオブジェクトの場合
const initialState = {
countA: 0,
countB: 0
};
const [countState, dispatch] = useReducer(reducer, initialState)
- dispatch
<Button onClick={()=>dispatch('incrementA')}>+1</Button>
<Button onClick={()=>dispatch('decrementA')}>-1</Button>
<Button onClick={()=>dispatch('incrementB')}>+10</Button>
<Button onClick={()=>dispatch('decrementB')}>-10</Button>
- reducer
const reducer = (countState, action)=> {
switch (action){
case 'incrementA':
return {...countState, countA: countState.countA + 1}
case 'decrementA':
return {...countState, countA: countState.countA - 1}
case 'incrementB':
return {...countState, countB: countState.countB + 10}
case 'decrementB':
return {...countState, countB: countState.countB - 10}
default:
return count
}
}
payloadを使う
dispatch関数側から、actionだけでなく、値を渡したいケースがあるかもしれません。payloadを使うことで実現できます。
- dispatch
<Button onClick={()=>dispatch({type: 'increment', payload: 5})}>increment</Button>
<Button onClick={()=>dispatch({type: 'decrement', payload: 5})}>decrement</Button>
- reducer
const reducer = (count, action)=> {
switch (action){
case 'increment':
return {count: state.count + action.payload}
case 'decrement':
return {count: state.count - action.payload}
default:
return count
}
}