useReducerを学びました

komosyu
React

目次

  1. はじめに
  2. useReducerとは?useStateとの比較
    1. 書き方の違い
    2. 実際の使い方
    3. 状態を更新する方法
    4. 状態と処理の分離
  3. 参考
  4. さいごに

はじめに

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: 状態の管理と更新方法

どういうことかというと、useStatesetStateを呼ぶ側で都度処理を行って状態を更新していきますが、useReducerは定義した時点で更新方法も設定してるので、処理を実行したい場合にはdispatch内の引数に特定の値を設定するだけで済んでシンプルにまとめることができるということで、状態と更新方法を一緒に管理する利点がわかると思います。

状態と処理の分離

useStatesetStateを使ってコンポーネント内で処理を行なっていますが、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>
        </>
    )
}

参考

さいごに

簡単ではありますが、今回はuseReducerについて解説していきました。

これまでuseStateで簡単な状態の管理しかしてきていなかったようでuseReducerを使う機会に恵まれませんでしたが、状態の保持と処理を一緒に管理できることの利点が理解できたので、これからは積極的にuseReducerを使っていこうと思いました!

まだまだReactでわかっていないことがたくさんあるので、これからもアウトプットとして記事を書いていこうと思います。