0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React: useReducer [アウトプット全くしてこなかったのでアウトプットする009]

Posted at

前回useStateをアウトプットしたので今日はuseReducerをアウトプットしていきます。

const [state, dispatch] = useReducer(reducer, initialArg, init);

上記はuseReducerの基本的な構文です。

useStateと見比べてみます。

// useState
const [state, setState] = useState(initialState);

// useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);

どちらも分割代入で第1引数にはstateを受け取っています。
第2引数は違いますがどちらもstateを更新させるための関数です。

ちなみに
const [state, setState] = useState(initialState);の書き方はこの記事を読むとよくわかります。

const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducerの方には第1引数に以下のようなstateの値を更新させる関数を渡し、

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

第2引数に以下のような初期値を渡します。

const initialState = {count: 0};

全体を見ると以下のようになります。

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

個人的にuseReducerの第一引数と第二引数を逆にしてくれた方が見やすいと思っています。

// 以下は誤った構文だがこちらの方が分割代入のstate(値),dispatch(関数)に
// useReducerの引数の並びを合わせているので見やすい
const [state, dispatch] = useReducer(initialArg,reducer);

半年くらい前に読んだ本でuseReducerを使ってuseStateでsetStateするのではなく、useReucerを使用するという方法があったので紹介します。

以下はチェックボックスをuseStateで管理しているあるあるなコードなのですが、ここでuseReducerを使えるそうです。

あるあるuseState管理

export function CheckBox() {
  const [checked, setChecked] = useState(false);

  return (
    <>
      <input
        type="checkbox"
        checked={checked}
        onChange={() => setChecked((checked) => !checked)}
      />
      {checked ? "checked" : "not checked"}
    </>
  );
}

useReducerを使って書き換える

export function CheckBox() {
  const [checked, toggle] = useReducer(checked => !checked,false);

  return (
    <>
      <input
        type="checkbox"
        checked={checked}
        onChange={() => toggle()}
      />
      {checked ? "checked" : "not checked"}
    </>
  );
}

今までuseStateを使用してコンポーネント部分からstateを参照していたのがなくなりかなりスッキリしました。
何をやっているかというと

const [checked, toggle] = useReducer(checked => !checked,false);

useReducer()の第1引数が関数でこれはtoggle関数になります。そして第二引数が初期値のfalseでありこれがcheckedの値になります。

またtrue,falseのtoggleだけでなく数値にも使用できます。

export function Numbers() {
  const [number, setNumber] = useReducer(
    (number, newNumber) => number + newNumber,
    0
  );

  return <h1 onClick={() => setNumber(3)}>{number}</h1>;
}

さらにオブジェクトの場合、もっと光ります。
以下もオブジェクトをuseStateで管理しているあるあるなコードなのです

export function User() {
  const firstUser = {
    id: "1234567",
    firstName: "cawa",
    lastName: "uchi",
    city: "nasu",
    state: "tochigi",
    email: "hogehoge@gmail.com",
    admin: false
  };
  const [user, setUser] = useState(firstUser);

  return (
    <div>
      <h1>
        {user.firstName} {user.lastName} - {user.admin ? "Admin" : "Usser"}
      </h1>
      <p>Email: {user.email}</p>
      <p>
        Location: {user.city}, {user.state}
      </p>
      <button
        onClick={() => {
          setUser({ ...user, admin: true });
        }}
      >
        Make Admin
      </button>
    </div>
  );
}

問題は以下の部分でsetUserの中でuserというオブジェクトを参照してスプレッド構文でマージしてそれを返しているのですが、...userを書き忘れるとuserの情報はadmin: trueのみになってしまいます。

<button
  onClick={() => {
    setUser({ ...user, admin: true });
  }}
>

もし以下のようにすると

<button
  onClick={() => {
    setUser({ admin: true });
  }}
>

以下のようにadmin以外の情報がなくなってしまう

user = {
  admin: true
}

ここでuseReducerを使用すると

export function User() {
  const firstUser = {
    id: "1234567",
    firstName: "cawa",
    lastName: "uchi",
    city: "nasu",
    state: "tochigi",
    email: "hogehoge@gmail.com",
    admin: false
  };
  const [user, setUser] = useReducer(
    (user, newDetails) => ({ ...user, ...newDetails }),
    firstUser
  );

  return (
    <div>
      <h1>
        {user.firstName} {user.lastName} - {user.admin ? "Admin" : "Usser"}
      </h1>
      <p>Email: {user.email}</p>
      <p>
        Location: {user.city}, {user.state}
      </p>
      <button
        onClick={() => {
          setUser({ admin: true });
        }}
      >
        Make Admin
      </button>
    </div>
  );
}

setUserからスプレッド構文をなくすことができました

<button
  onClick={() => {
    setUser({ admin: true });
  }}
>

実務でもどんどんuseReducerを使っていきたいと思いました!

参考

強くなりたい!!!!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?