前回の記事ではReactのuseStateの正しい使い方を説明しました。
今回はReact Hooksのうちの一つであるuseReducerの使い方を説明します。
reducerとは
まずは、JavaScriptのreduceメソッドについておさらいしましょう。
reduceは英語で減らすという意味ですね。イメージとして、配列の複数データを操作して1つの値に減らすためのメソッドであると考えてください。
useReducerも同じようにState(状態)を管理できるメソッドになります。
配列の数字をループして加算したい場合はこのようにできます。
const numbers = [10, 20, 30]; let total = 0; for (const n of numbers) { total += n; } export default function App() { return <div className="App">{total}</div>; }
これで、ブラウザに合計の60が表示されました。

同じことをreduceメソッドを使って行ってみます。
reduceメソッドは2つのパラメータを受け取ります。
reduce(()=>,0)
最初のパラメータは関数で、2つ目が初期値(スタートポイント)になります。
// import { useState } from "react"; const numbers = [10, 20, 30]; let total = 0; function reduceNum() { total = numbers.reduce( (accumulator, currentValue) => accumulator + currentValue, 0 ); return total; } reduceNum(); export default function App() { return <div className="App">{total}</div>; }
これで同じように合計値の60がブラウザに表示されました。
reduce()関数の最初に使う関数では①accumulatorと②currentValueがパラメータとして使えます。
accumulatorはaccumulate(蓄積する)という意味です。つまり、今までの数値を累積(足した)値になります。いわば、前回までの累積値といえます。
2番目に来るcurrentValueは現在の値です。このようにreduceメソッドでは前回までの累積値と現在の値をどうするか、指示することができます。
今回の場合は単純に配列の前回の値(0番目)と現在地(1番目)を足すという指示をしています。
その次に配列の前回の値(0と1番目の累積値)と現在地(2番目)を足す。これを繰り返しているだけです。
これでreduceメソッドの使い方が理解できましたね。
useReducer
ReactのuseReducerは基本はこのようになります。
const [state, dispatch] = useReducer(reducer, initialArg, init?)
useReducerからは2つの値が返ってきます。それを慣例的にstateと、dispatchと名称を付けて取っておきます。
useReducerがreturn返す値
state:現在の状態。最初のレンダーでは、初期値のinitialArg、もしくはinit(initialArg)が返ってきます。
dispatch:どのようにコンポーネントを再レンダーし、stateを更新するか指示する関数
useReducer関数の引数について
reducer:reducerでは、stateがどのようにアップデートされたいのか指示することができます。
initialArg:デフォルト値(データタイプに指定はありません。)
init?:オプショナルの引数です。もしinitialArgに何かしらの関数を実行したい場合はここで指示できます。その場合はinit(initialArg)になります。もし空の場合は、initialArgはデフォルト値として渡されます。
useReducerの実例
では下記の実例を見てみましょう。
import { useReducer } from "react"; export default function App() { function reducer(state, action) { switch (action.type) { case "SET_NAME": return { ...state, name: action.payload }; } } const intialArg = { names: [], name: "", }; const [state, dispatch] = useReducer(reducer, intialArg); return ( <div className="App"> <input type="text" value={state.name} onChange={(e) => dispatch({ type: "SET_NAME", payload: e.target.value }) } /> <div>name = {state.name}</div> </div> ); }
このようにinputへの入力と同時にstate.nameの状態が更新されコンポーネントが再度レンダーされました。

inputに入力されれるたびにonChangeのdispatchが発火されpayload(データ)が更新されます。dispatchはuseReducerのstateを更新する役目があるので、コンポーネントが更新されたわけですね。
次に、ボタンを作成して、state.namesにnameのデータを追加していきましょう。
import { useReducer } from "react"; export default function App() { function reducer(state, action) { switch (action.type) { case "SET_NAME": return { ...state, name: action.payload }; case "ADD_NAME": return { ...state, names: [...state.names, state.name], name: "", }; } } const intialArg = { names: [], name: "", }; const [state, dispatch] = useReducer(reducer, intialArg); return ( <div className="App"> <input type="text" value={state.name} onChange={(e) => dispatch({ type: "SET_NAME", payload: e.target.value }) } /> <div>name = {state.name}</div> <button onClick={() => dispatch({ type: "ADD_NAME" })}>Add Name</button> <div> {state.names.map((name, index) => ( <div key={index}>{name}</div> ))} </div> </div> ); }
…のスプレッド構文を使って、配列に現在のstateを残したまま、新しいデータを追加しています。
これで、データの入力時に今までのデータを付けるので”追加”できるようになります。

このようにuseReducerを使うことで複雑なstate(状態)の管理が可能になります。