UseReducerの基本的な使い方
はじめに
UseReducerはUseStateと同じようにコンポーネントの中にstateと呼ばれる状態と初期値を定義するところまでは同じ。
UseStateと異なるのは定義した時点で状態の値に対して、どんな更新を行うのか定義できるところ。
使用例
import { useState, useReducer } from "react"
const Example = () => {
// (2)
const rFunction = (prev, {action, value}) => {
switch(action) {
case "up":
return prev + value;
case "down":
return prev - value;
default:
throw new Error("定義されていないactionです。");
}
};
// (1)
const [rstate, dispatch] = useReducer((prev, { action, value }) => {
rFunction(prev, { action, value })
}, 0);
// (3)
const rAddCount = (value) => {
// 初期値が決められた段階で既にどんな更新を行うのか定義済み
dispatch({ action: "up", value: value });
};
const rMinusCount = (value) => {
dispatch({ action: "down", value: value });
}
return (
<>
<div>
<h3>{ rstate }</h3>
<button onClick={ () => rAddCount(1) }> +1 </button> <!-- (4) -->
<button onClick={ () => rAddCount(2) }> +2 </button>
<button onClick={ () => rMinusCount(1) }> -1 </button>
<button onClick={ () => rMinusCount(2) }> -2 </button>
</div>
</>
);
};
export default Example;
(1)定義
const [rstate, dispatch] = useReducer((prev, { action, value }) => {
rFunction(prev, { action, value })
}, 0);
useReducerを呼び出した後、第一引数に更新を実行する関数を、第二引数に初期値を設定。
第一引数に渡す更新処理が返り値のdispatchメソッドを呼び出した時に行う処理になる。
今回の場合、
関数は(prev, { action, value }) => { rFunction(prev, { action, value }) }の部分。
初期値は0。
関数についてprevは前の状態の値。{ action, value }はdispachメソッドが引数として受け取る値。
dispachメソッドが呼ばれた後、内部でrFunctionメソッドが呼ばれる。書き方によってはここに直接、処理をアロー関数で記述してしまっても問題ない。
(2)実際の更新処理
const rFunction = (prev, {action, value}) => {
switch(action) {
case "up":
return prev + value;
case "down":
return prev - value;
default:
throw new Error("定義されていないactionです。");
}
};
(prev, { action, value }) => { rFunction(prev, { action, value }) }の記述部分でrFunctionにprev(前の状態の値)とaction(処理)とvalue(前の状態の値に与える影響)のプロパティを持つオブジェクトを渡す。
※action(処理)を引数に渡す処理は今の所よく見受けられるが、value(前の状態の値に与える影響)を渡して良いものかまだ不明。
処理内容としてはactionがupならvalue分、加算。actionがdownならvalue分、減算。
(3)dispatchの利用
const rAddCount = (value) => {
// 初期値が決められた段階で既にどんな更新を行うのか定義済み
dispatch({ action: "up", value: value });
};
const rMinusCount = (value) => {
dispatch({ action: "down", value: value });
}
...中略
<button onClick={ () => rAddCount(1) }> +1 </button>
<button onClick={ () => rMinusCount(1) }> -1 </button>
ボタンを押した際に() => rAddCount(1)、もしくは() => rMinusCount(1)を実行。
それぞれのメソッドの内部でdispatchメソッドにactionとvalueを渡して、さらに内部のrFunctionを呼び出している。
こんな書き方をしても良いのか?
const countControle = (action, value) => {
dispatch({ action: action, value: value });
};
...中略
<button onClick={ () => countControle("up", 1) }> +1 </button>
<button onClick={ () => countControle("down", 1) }> -1 </button>
さらに
直接dispatchを呼んでも。
これが一番わかりやすいかも?
<button onClick={ () => dispatch({ action: "up", value: 4 }) }> +4 </button>
おまけ... (4)イベントハンドラーに渡す引数を渡す時
return (
<>
<div>
<h3>{ rstate }</h3>
<button onClick={ () => rAddCount(1) }> +1 </button> <!-- (4) -->
<button onClick={ () => rAddCount(2) }> +2 </button>
<button onClick={ () => rMinusCount(1) }> -1 </button>
<button onClick={ () => rMinusCount(2) }> -2 </button>
</div>
</>
);
今まで<button onClick={rMinusCount}> -1 </button>の書き方をしていたが
引数を渡したい時には<button onClick={ () => rMinusCount(1) }> -1 </button>のように中でアロー関数を定義し、さらにその内部でrMinusCountメソッドを引数付きで呼び出せば良い。
UseStateとの違い
-
UseStateは初期値の定義のみだが、UseReducerは初期値に加えて更新方法も定義時に指定することができる。- 定義している箇所が一箇所なのでコードの見通しが良くなる。
- 更新方法が管理しやすくなるので開発規模が大きくなった時に威力を発揮する。
Discussion