ReactのuseStateとuseReducerを比較する。

2023/04/06に公開約3,100字

はじめに

Reactの状態管理のHookを比較して、それぞれの用途をまとめる。

カウンター

useState

useStateを用いたカウンターの実装を以下に示す。

import { useState } from "react";

function Counter1() {
  const [count, setCount1] = useState(0);
  const increment = (payload) => {
    setCount1((prevCount) => prevCount + payload);
  };
  return (
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => increment(1)}>+</button>
      <button onClick={() => increment(-1)}>-</button>
    </>
  );
}

useReducer

useReducerを用いたカウンターの実装を以下に示す。

import { useReducer } from "react";

function Counter2() {
  const [count, dispatch] = useReducer((state, action) => state + action, 0);
  return (
    <>
      <h1>Count: {count}</h1>
      <button onClick={() => dispatch(1)}>+</button>
      <button onClick={() => dispatch(-1)}>-</button>
    </>
  );
}

比較

useStateの場合、状態の更新関数を別に定義する。setCountを更新関数を入れて作る等の工夫をして気をつけて実装しないといけない。useReducerの場合、状態の更新関数を同時に定義できるため、記述量が圧倒的に少なく出来る。

2つのカウンター

useState

useStateで2つのカウンター作成。

function DoubleCounter1() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  const increment1 = (payload) => {
    setCount1((prevCount) => prevCount + payload);
  };
  const increment2 = (payload) => {
    setCount2((prevCount) => prevCount + payload);
  };

  return (
    <>
      <h1>Count1: {count1}</h1>
      <button onClick={() => increment1(1)}>+</button>
      <button onClick={() => increment1(-1)}>-</button>
      <h1>Count2: {count2}</h1>
      <button onClick={() => increment2(1)}>+</button>
      <button onClick={() => increment2(-1)}>-</button>
    </>
  );
}

useReducer

useReducerで2つのカウンター作成。

function DoubleCounter2() {
  const [state, dispatch] = useReducer(
    (state, action) => {
      switch (action.type) {
        case "increment1":
          return { ...state, count1: state.count1 + action.payload };
        case "increment2":
          return { ...state, count2: state.count2 + action.payload };
        default:
          return state;
      }
    },
    { count1: 0, count2: 0 }
  );

  return (
    <>
      <h1>Count1: {state.count1}</h1>
      <button onClick={() => dispatch({ type: "increment1", payload: 1 })}>
        +
      </button>
      <button onClick={() => dispatch({ type: "increment1", payload: -1 })}>
        -
      </button>
      <h1>Count2: {state.count2}</h1>
      <button onClick={() => dispatch({ type: "increment2", payload: 1 })}>
        +
      </button>
      <button onClick={() => dispatch({ type: "increment2", payload: -1 })}>
        -
      </button>
    </>
  );
}

比較

useStateの場合、それぞれの状態を個別に定義する。useReducerの場合コンポーネント内の全ての状態を一箇所で定義し更新関数も定義している。
この場合はそれぞれのボタンが分離しているので、コードの記述量が短いuseStateの方が楽に見える。ただ、コンポーネント内の状態が複雑に絡み合うような場合においてはuseReducerの方が向いてるかもしれない。そもそも状態が完全に分離しているならコンポーネントも分けて実装すべきなので、実用上前者は起こりにくいと思う。

おわりに

ReactのuseStateとuseReducerを用いてカウンターを実装した。useStateの場合、静的にセットするか更新関数にするかを選んで実装しないといけないかわりに、コードの記述量が少ないというメリットがある。一方で、useReducerの場合、そもそもが更新関数として定義するので実装上考えることは少ないが、その分記述量が多いというデメリットがある。実用上はuseReducerの方が書きやすくはあると思った。

Discussion

ログインするとコメントできます