最初にまとめ
hooks | 使用タイミング |
---|---|
useState | 簡単な状態管理・処理 |
useReducer | 分岐が発生するなど、複雑な処理である・テストが必要な場合 |
useReducerとは
- 状態を扱うためのフック(useStateと似ている)
- useReducerは宣言時に、条件分岐と処理内容を予め定義する
- 条件によって状態の更新を分岐できたり、状態の更新関数をコンポーネントの外に切り出すことができる
使い方
- action で受け取った値に応じて、条件分岐・処理を行い、各々の処理結果を返す
const [state, dispatch] = useReducer((prev, action) => {条件式}, 初期値)
state | 現在の状態の値 |
---|---|
dispatch | reducerを実行するための呼び出し関数(変数を宣言時、stateの更新方法をあらかじめ設定できる) |
reducer | stateを更新するための関数(prev, action) => {条件式} の箇所 |
action | 状態を変化させる上での条件(利用側 dispatchからオブジェクトが渡ってくる。switch文で処理の分岐を書くのが一般的) |
呼び出し方
const test = () => {
dispatch({ /*actionに渡すオブジェクト*/ });
};
useStateとuseReducerの違い
プログラムの観点から
「状態の更新の仕方を誰が担当するか」によって使い分ける
- useStateは、宣言時に初期値しか設定しないため、状態更新する側が、更新方法を定義する
- useReducerは、宣言時に更新方法を定義するため、状態更新する側は、dispatchは更新方法を指定するだけ
大規模なアプリケーション開発の現場では、
useReducerを使って、state宣言時に更新方法も定義する方が見通しの良いコードになる。
関数型プログラミングの観点から
- state の変更ロジックを
reducer
に切り離すことができる - useReducerは「純粋関数」であるため、単体テストがしやすい。
useStateは、状況によって、不変性のある関数になりうる。
useReducerを使って簡単なカウンターを作ってみる
import { useReducer } from "react";
export default function App() {
// 処理内容を定義
const reducer = (prev: number, action: { type: string; step: number }) => {
const { type, step } = action;
switch (type) {
case "countUp":
return prev + step;
case "countDown":
return prev - step;
default:
throw new Error("不明なactionです");
}
};
// useReducerの使用を宣言
const [state, dispatch] = useReducer(reducer, 0);
// カウントアップ関数
const onClickReducerCountUp = () => {
dispatch({ type: "countUp", step: 3 }); // ※オブジェクトで渡す
};
// カウントダウン関数
const onClickReducerCountDown = () => {
dispatch({ type: "countDown", step: 4 });
};
return (
<div>
<p>{state}</p>
<button onClick={onClickReducerCountUp}>カウントアップ</button>
<button onClick={onClickReducerCountDown}>カウントダウン</button>
</div>
);
}
useState
を多用していましたが、
処理が増えるならば、積極的にuseReducer
を利用した方が、
コードの見通しがだいぶ変わるので、積極的に利用しようと思いました!