目次
はじめに
Next.jsを触っていて、なんとなくノリでReactを使っていたところがあったので、よくわからないところをひとつずつ潰していこうと思っています。
今回はその第一弾として、useReducer
について解説していきます。
(まあ、ただUdemyで勉強したことをアウトプットしたいだけではありますが。。)
useReducerとは?useStateとの比較
useReducer
とはReactのhooksの中のひとつで、useState
のように状態を扱うものです。
公式ドキュメントでもそれぞれの違いについての解説さがれているので、興味のある方は読んでみてください。
書き方の違い
まず書き方から見ていくと、useState
とはstateを使う時の更新方法に違いがあります。
下記のコードのように、useState
ではsetState
のコールバック関数で処理を記述しますが、useReducer
では定義した時点で処理を記述し、dispatchによってuseReducerの第一引数が実行されるようになっています。
// useStateの場合
const [state, setState] = useState(0)
const stateCountUp = () => {
setState(prev => prev++);
}
// useReducerの場合
const [reducerState, dispatch] = useReducer(prev => prev++, 0)
const reducerCountUp = () => {
dispatch()
}
実際の使い方
それでは、先ほど作成したuseReducer
の処理を実際にCountコンポーネント
で使ってみましょう!
詳しい処理内容は以下のコード内に記述しております。
内容としては、カウントアップとカウントダウンのボタンを用意してクリックするごとに数字の大きさが変わるといったものになります。
const Count = () => {
// prevにはstate,actionにはdispatchの引数が入る
const [reducerState, dispatch] = useReducer((prev, action) => {
switch(action) {
// actionの値(dispatchの引数)が"up"であれば
case("up"):
// 元もprevの値に+1された値がreducerStateにセットされる
return ++prev
// actionの値(dispatchの引数)が"down"であれば
case("down"):
// 元もprevの値に-1された値がreducerStateにセットされる
return --prev
default:
throw new Error("error")
}
}, 0)
const reducerCountUp = () => {
dispatch("up")
}
const reducerCountDown = () => {
dispatch("down")
}
return(
<>
// useReducerで操作された値を表示
<div>{reducerState}</div>
// 値を操作
<button onClick={reducerCountUp}>カウントアップ</button>
<button onClick={reducerCountDown}>カウントダウン</button>
</>
)
}
状態を更新する方法
ざっくりと比較するとこのようになります。
- useState: 状態の管理のみ
- useReducer: 状態の管理と更新方法
どういうことかというと、useState
はsetState
を呼ぶ側で都度処理を行って状態を更新していきますが、useReducer
は定義した時点で更新方法も設定してるので、処理を実行したい場合にはdispatch
内の引数に特定の値を設定するだけで済んでシンプルにまとめることができるということで、状態と更新方法を一緒に管理する利点がわかると思います。
状態と処理の分離
useState
はsetState
を使ってコンポーネント内で処理を行なっていますが、useReducer
は定義した時点で状態と処理を管理しているためコンポーネントの外側に処理を書くことができてコードの管理もしやすくなります。
また、入ってくる引数が決まっているので下記のように変数reducer
としてコンポーネント外に処理を書き出してコードを見やすく整理することもできます。
// prevにはstate,actionにはdispatchの引数が入る
const reducer = (prev, action) => {
switch(action) {
case("up"):
return ++prev
case("down"):
return --prev
default:
throw new Error("error")
}
}
const Count = () => {
const [reducerState, dispatch] = useReducer(reducer, 0);
const reducerCountUp = () => {
dispatch("up")
}
const reducerCountDown = () => {
dispatch("down")
}
return(
<>
<button onClick={reducerCountUp}>カウントアップ</button>
<button onClick={reducerCountDown}>カウントダウン</button>
</>
)
}
今回はカウントのup,downだけだったので引数がひとつですみましたが、複数の引数が入る場合はこんな感じの書き方にすることができます。
// prevにはstate,actionにはdispatchの引数が入る
// dispatchに複数の値が入る場合、action.type,action.valueのような感じでそれぞれの値を取り出せます
const reducer = (prev, action) => {
switch(action.type) {
case("up"):
return prev + action.value
case("down"):
return prev - action.value
default:
throw new Error("error")
}
}
const Count = () => {
const [reducerState, dispatch] = useReducer(reducer, 0);
// dispatchの引数にはこんな感じでオブジェクト内に複数の値を持たせることができます
const reducerCountUp = () => {
dispatch({type: "up", value: 5})
}
const reducerCountDown = () => {
dispatch({type: "down", value: 10})
}
return(
<>
<button onClick={reducerCountUp}>カウントアップ</button>
<button onClick={reducerCountDown}>カウントダウン</button>
</>
)
}
参考
- https://beta.reactjs.org/reference/react/useReducer
- https://beta.reactjs.org/learn/extracting-state-logic-into-a-reducer#comparing-usestate-and-usereducer
- https://www.udemy.com/share/106Nqw3@17wneCmC7u77GFgtqR132aAAZBpQECHRhGEFJPRI-8wDQQTLHpX4sATtcd_IZof8ww==/
- https://reffect.co.jp/react/react-hook-reducer-understanding
さいごに
簡単ではありますが、今回はuseReducerについて解説していきました。
これまでuseStateで簡単な状態の管理しかしてきていなかったようでuseReducerを使う機会に恵まれませんでしたが、状態の保持と処理を一緒に管理できることの利点が理解できたので、これからは積極的にuseReducerを使っていこうと思いました!
まだまだReactでわかっていないことがたくさんあるので、これからもアウトプットとして記事を書いていこうと思います。