1
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?

お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

ずっと気になってたuseStateの更新用関数の使い道について考える

Posted at

はじめに

Reactを学習し始めたころ、useStateのset関数には更新用関数を設定することができると学びました。

しかし、今のところ業務でそれらしきコードを見たことがありません。
また、公式サイトで示されている例を見ても、あまりその実用性がピンときていません。

そこで、どんな場面で使えるのかを考えてみます。

更新用関数とは

次のような場面を考えてみます。

コードは以下の通りです。

App.tsx
import { useState } from "react";

const App = () => {
  const [state, setState] = useState(0);
  const handleClick = () => {
    setState(state + 1);
    setState(state + 1);
    setState(state + 1);
  };
  return (
    <>
      <div>{state}</div>
      <button onClick={handleClick}>count up</button>
    </>
  );
};

export default App;

setStateを3回呼び出して、stateを3回更新しています。
初期値が0なので、1回のボタンクリックで3ずつ増える想定です。

しかしボタンを押すとわかりますが、実際には1しか増えません。

これは、setStateを実行した時点ではまだstateは更新されていないためです。
更新前のstateを参照しているので、何度setStateを呼び出しても初期値の0に対して1を追加する、という処理を繰り返しているだけです。

このような挙動を回避するため、setStateにわたすのが更新用関数です。

以下のようにsetStateに関数を渡すことで、参照するstateが更新後のstateであることを保証してくれます。

handleClickのみ抜粋
  const handleClick = () => {
    setState((prevState) => prevState + 1);
    setState((prevState) => prevState + 1);
    setState((prevState) => prevState + 1);
  };

更新用関数は引数に直前のstateを受け取ります。
変数名は自由ですが、一般的にはstateの変数の1文字目、または頭にprevとつけるそうです。

結果は以下のようになります。

というのが更新用関数の説明です。

更新用関数、使うか?

さて、更新用関数が何であるかを改めて確認しましたが、これって使うんでしょうか。
上記で紹介したようなコードは実際のお仕事ではまず見かけません。
基本的には更新したい値をそのままset関数に指定すれば、それでオッケーな気がします。

よって、あまり使い道はないように思っています。
実際、公式サイトでも次のように言っています。

ほとんどのケースでは、どちらのアプローチでも違いはありません。

ということなので、基本的にはあまり意識しなくてもよさそうです。
使う場面としては以下のような場面とのこと。

一方で、同じイベント内で複数回の更新を行う場合、更新用関数が役に立ちます。また、state 変数自身を参照することが難しいケースにも有用です

ただ、これってどういう場面なんでしょう
ChatGPTの力も借りながら、ちょっと考えてみます。

同じイベント内で複数回の更新を行う

考えられるのは、「複数のチェックボックスをまとめてチェックする」でしょうか。

以下はコード例です。
※TypeScriptで書いています。

App.tsx
import React, { useState } from "react";

interface ListItem {
  id: number;
  text: string;
  checked: boolean;
}

const App: React.FC = () => {
  const [items, setItems] = useState<ListItem[]>([
    { id: 1, text: "アイテム 1", checked: false },
    { id: 2, text: "アイテム 2", checked: false },
    { id: 3, text: "アイテム 3", checked: false },
  ]);

  const checkAllItems = () => {
    setItems((prevItems) =>
      prevItems.map((item) => ({ ...item, checked: true }))
    );
  };

  const handleChange = (id: number) => {
    setItems((prevItems) =>
      prevItems.map((item) =>
        item.id === id ? { ...item, checked: !item.checked } : item
      )
    );
  };

  return (
    <div>
      <button onClick={checkAllItems}>まとめてチェック</button>
      <ul>
        {items.map((item) => (
          <li key={item.id}>
            <label>
              <input
                type="checkbox"
                checked={item.checked}
                onChange={() => handleChange(item.id)}
              />
              {item.text}
            </label>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default App;

画面としては以下のようになります。

と、書いていて気づきましたが、これ実際は複数回の更新ではなさそうですね。
set関数を呼んでいるのは一度だけなので、ちょっと違いそうです。

ただ、ポイントとしてはcheckAllItemshandleChangeともに更新用関数を使用して値を更新しているという点です。

これにより、直前がどのような状態であっても、更新後のstateを使って確実に処理をしてくれるので、とくにまとめてチェックの場合にはおかしな結果とならずに済みそうです。

state変数自身を参照することが難しい場合

これはちょっと私の文章読解能力では理解しきれなかったのですが、自分自身に対して繰り返し処理をするものだと理解すると、例えば経過時間を表示するタイマーとかが考えられるかもしれません(あまり実用的ではないですが)

以下は間違ったコード例です

App.tsx
import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1);
    }, 1000);

    return () => {
      clearInterval(timer);
    };
  }, []);

  return <div>{count}秒経過</div>;
}

export default App;

これを画面に表示させるとわかりますが、ずっと1秒のままです。

これを、更新用関数を設定すると以下のようになります。

timer関数のみ抜粋
    const timer = setInterval(() => {
      setCount((prevCount) => prevCount + 1);
    }, 1000);

更新用関数によって、更新後の値であることが保証されるので以下のように秒数が増えていきます。
image.png

まとめ

なんとか更新用関数の使い道を探ろう、と意気込んでChatGPTやClaudeAIにも聞いて色々考えてみましたが、正直な話絶対に更新用関数を使わなければならない場面というのはそうそうないように思いました。

最後のタイマーは確かに更新用関数がないと難しいかもしれませんが、これが現実的に生きてくる場面はあまりない気がしました。

なんとなく、徒労に終わった感じも否めないですが、今回は以上です。

1
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
1
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?