👌

非同期処理はイベントハンドラーで書いてもいい

2024/07/02に公開

初心者の自分としては迷うところだったので,備忘録を兼ねて記事を書きました.

Reactコンポーネント内での非同期処理

Reactではコンポーネントを宣言的に記述します.コンポーネントはレンダリング処理に関する操作(副作用)がない純粋関数として定義することが推奨されています.しかし実際はコンポーネントのイベント処理などでデータフェッチやDOM操作などレンダリング処理以外の操作が必要となることが少なくありません.

例えばボタンを押したときにあるサーバーからデータをフェッチするコンポーネントがあるとします.データフェッチなどの非同期処理はコンポーネントのレンダリングに直接関係のある処理ではないため,これらは副作用にあたります.

useEffect

このような副作用の処理はuseEffect内で記述して実行することが可能ですuseEffectではコンポーネントのライフサイクルとは別に,特定の変数を監視対象とすることで,値の変更をトリガーとしてそのコードブロック内の処理を実行することができます.

何かがおかしい

ここで「useEffectでは副作用を記述することができます」という記事を読んだ私は「非同期処理は副作用だからuseEffectで書くんだな!」と思い込みました.そして以下のようなコードを書いていました.

function MyButton(): React.JSX.Element {
  const [state, setState] = useState<boolean>(false);

  useEffect(() => {
    const func = async () => {
      const res = await fetch("hoge");
      // ...
    };
    func();
  }, [state]);

  function handleClick() {
    setState((state) => !state);
  }

  return <button type="button" onClick={handleClick}>Click!</button>
}

あれ...こんなにややこしく書かないといけないんだっけ...?

イベントハンドラー

さすがに何かおかしいと思った私は,本当に副作用はuseEffectにしか書かないといけないのか調べました.

https://qiita.com/namn1125/items/d420ae48c19a3d7c8248

Should I use useEffect or a handler for async calls? : r/reactjs

どちらの記事にも書かれているように.副作用である非同期処理はイベントハンドラー内で書けます.そもそも,イベント自体がコンポーネントの描画とは関係のない処理であり,副作用そのものです.イベントハンドラー内で副作用を書くほうが自然に感じます.

イベントハンドラー自体をasyncにして書き換えると以下のようになりました.こちらの方がコードもすっきりしていて良いですね.

function MyButton(): React.JSX.Element {
  async function handleClick() {
    const res = await fetch("hoge");
      // ...
  }

  return <button type="button" onClick={handleClick}>Click!</button>
}

使い分け

ではuseEffectはどのようなときに使用すればよいのでしょうか.先にあげた2つの記事によると,基本的にはイベントハンドラーを扱い,それでは対応できない特殊なケースでuseEffectを使うのが良いようです.

  • ボタンクリックなどのイベントがトリガーになる処理はイベントハンドラーに書く.
  • レンダリングやステートの変更をトリガーとしたい処理はuseEffectに書く.
  • クリーンアップ処理が必要な場合はuseEffectに書く.

まとめ

副作用を起こす処理がイベント駆動の場合は,イベントハンドラーに書きましょう.そうでなくレンダリングのタイミングに依存する場合はuseEffectを使うことができます.

Discussion