useReducerの使いどころが分かってきた
テーブル一覧画面の作成を例に説明する。
こんなタイミングでデータを再取得することになる。
ページネーションで「前へ」「次へ」「先頭へ」「末尾へ」のボタンクリック
表示件数(10件・50件・100件…)を変更
フィルター検索に入力
code:Example.jsx
でもこれだと「あるステートの変化により別のステートも変化する」という複雑な状態に…。
例えば表示件数(pageLimit)を変更すると、表示範囲(pageRange)も変更する必要があるし。
フィルターの入力値(filterVal)が変わったら、表示範囲(pageRange)も最初のページにリセットしてあげないと。
ステートの変化を機にAPIを実行してデータ取得すると、1つの結果を求めるために無駄に複数回APIが実行されちゃう…。
表示件数を変更したときだけ前のページの表示がガッツリ消えて初回のローディングと同じ状態になってしまい…。
これはダメなんだと気づいた。
code:Example.jsx
// pageLimitのステートが変化したらpageRangeをセットし直してね
useEffect(() => {
setPageRange((prev) => ({...prev, end: prev.end + pageLimit}));
React Docsにも「外部システムが関与しない場合(例えば、propsや状態が変化したときにコンポーネントの状態を更新したい場合)、Effectは必要ありません」とある。この解説に出てくるNG例と同じことをしてしまった…。
useReducerも同じくステートを管理するReact hooksのひとつ。
ちがいは、useReducerは状態の変更方法まで一元管理できること。
複数の状態が絡み合う複雑な状態管理を行うときに使えるものだと理解した。
code:Example.jsx
// よく考えたらpageLimitは "end - start" で算出できるな…消した
// (すでにある値で算出できるものを状態管理するべからず、らしい)
const initialState = { start: 0, end: 10, filterVal: ""}
// ステートの変更方法はこのreducerで一元管理できる
const reducer = (state, action) => {
switch(action.type) {
// 下のhandleChangeLimitのdispatchに対応するアクション
case "changeLimit":
return { ...state, end: state.end + action.limit };
// ほかにも先頭へのボタンクリック時のアクションとか… case "goToTop": {
const limit = state.end - state.start;
return { ...state, start: 0, end: limit };
}
// あと末尾へ次へ前へとかフィルターのアクションを続けてそれぞれ書いていく…略 }
}
const Example = () => {
// ステートはこれひとつに集約された
// 表示件数を示す<select>タグのonChangeからこの関数を呼び出すイメージ
const handleChangeLimit = (e) => {
dispatch({type; 'changeLimit', limit: e.target.value })
}
// 以下省略
}
Reactを業務で使い始めて1ヶ月の私の気づきでした。