DIVX テックブログ

catch-img

一言で表すと同じ機能。ではどう使い分ける? useEffectとuseCallbackの使い分け


目次[非表示]

  1. 1.はじめに
  2. 2.違いを比較するミニアプリ
  3. 3.useEffectの基本構造
  4. 4.useEffectでの実装
  5. 5.解説
  6. 6.useCallbackの基本構造
  7. 7.useCallbackでの実装
  8. 8.解説
  9. 9.違いは?
  10. 10.どんな場面で使用するのがいい?
  11. 11.終わりに

こちらの記事はDIVXアドベントカレンダー2023の5日目の記事です。

はじめに

こんにちは。名古屋拠点の長屋です。
今回はReactのuseEffectとuseCallbackの使い分けについて書いていこうと思います。
どちらも一言で表すと"着目している値が変化した時に関数を実行させる"機能ですが、どんな時にどちらを使うといいのか、気になったので深掘ってみようと思います。

違いを比較するミニアプリ

ボタンを押すと数字が増えて、数字が偶数の時だけ文字の色が赤くなるアプリを作ります。
useEffect、useCallback、それぞれを使用して作ってみます。
挙動は以下のようになります。


useEffectの基本構造

useEffect(実行する関数, [着目したい値]);

React Hooksの基本のフックの一つであり、副作用 (effect) フックです。
(副作用とはJSXを返す以外の処理。DOMによる更新、参照や、state(状態)の更新のことです。)
[]に変数などの値を入れて、その値が変化した時に関数を実行できます。
[]に何も入れずに空配列で使用すると、最初の一回だけ実行したい処理を実行できます。
この方法はバックエンドからデータを取得する時などに使われます。

useEffectでの実装

import React, { useEffect, useState } from "react";

const App = () => {
  const [num, setNum] = useState(0);
  const [color, setcolor] = useState("black");

  const onClickCountUp = () => {
    setNum(num + 1);
  };

  useEffect(() => {
    if (num > 0) {
      if (num % 2 === 0) {
        setcolor("red");
      } else {
        setcolor("black");
      }
    }
  }, [num]);

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
        height: "100vh"
      }}
    >
      <h1>偶数の時に赤字になるよ</h1>
      <button onClick={onClickCountUp}>
        PUSH!
      </button>
      <br />
      <h1 style={{ color: color }}>{num}</h1>
    </div>
  );
};

export default App;

解説

ボタンを押すとonClickによりonClickCountUpが発火します。
onClickCountUpはnum(初期値は0)の値に1を足します。

useEffect(() => {
    if (num > 0) {
      if (num % 2 === 0) {
        setcolor("red");
      } else {
        setcolor("black");
      }
    }
  }, [num]);

useEffectは第2引数の値を見張り、変化があったときに第1引数の関数を実行させます。
この場合numの値が変化すると、if文が動いて偶数であると文字が赤くなります。

useCallbackの基本構造

const 関数名 = useCallback(実行する関数, [着目したい値]);

useCallbackは関数のメモ化をします。
関数が毎回呼ばれてしまうのを防ぐことができます。
useEfffectと同じく、[]の値が変化すると第一引数の関数が実行されます。

useCallbackでの実装

import React, { useCallback, useState } from "react";

const App = () => {
  const [num, setNum] = useState(0);
  const [color, setcolor] = useState("black");

  const onClickCountUp = useCallback(() => {
    const newCount = num + 1;
    setNum(newCount);

    if (newCount > 0) {
      if (newCount % 2 === 0) {
        setcolor("red");
      } else {
        setcolor("black");
      }
    }
  }, [num]);

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
        height: "100vh"
      }}
    >
      <h1>偶数の時に赤字になるよ</h1>
      <button onClick={onClickCountUp}>
        PUSH!
      </button>
      <br />
      <h1 style={{ color: color }}>{num}</h1>
    </div>
  );
};

export default App;

解説

  const onClickCountUp = useCallback(() => {
    const newCount = num + 1;
    setNum(newCount);

    if (newCount > 0) {
      if (newCount % 2 === 0) {
        setcolor("red");
      } else {
        setcolor("black");
      }
    }
  }, [num]);

ボタンが押されるとnewCountが動き、numに1が足されます。
newCountはsetNumの引数としてsetNumの状態を更新します。
その後、if文が動いて偶数の時に文字色が変化します。

違いは?

画面上の動きは同じですが、コードの仕組みはどこが違うか考えてみました。

  • useEffect使用はuseEffect内には色が変わる処理しか入っていない。
  • useCallback使用はnum + 1 から色が変わるまでの処理が1つの関数内に入っている。

この違いより、useEffectとuseCallbackをどんな場面で使うと適切か考えてみました。

どんな場面で使用するのがいい?

  • useEffect

1度だけ読み込み、または特定の値の変更時が必要な時。
[]に空配列を指定して、データを取得する時に使うのが適切だと考えられます。
例として、記事投稿サイトアプリケーションの記事一覧表示機能部分で使用した例を載せます。

useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get<PostWithIdType[]>('http://localhost:3001/posts');
        setPosts(response.data);
      } catch (error) {
        setError(true);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, []);

このuseEffect関数は第2引数に空配列を指定しています。つまり着目する値がないのでコンポーネントがマウントされた時に1度だけ第1引数の関数が実行されます。結果として何度もレンダリングすることなく、axios通信を使用してバックエンド(http://localhost:3001/posts )から1度だけデータを読み込んで表示することができます。

  • useCallback

関数が何度も使い回される場合、関数化をしておくと便利です。useCallbackを使用することで、関数をメモ化して、再利用することができます。
例えば記事投稿サイトアプリケーションアプリケーションのスレッドのタイトル編集機能を作る際にuseCallbackを使用しました。

A.tsx
const updateTitle = (newTitle) => {
    setTitle(newTitle);
  };
...
< updateTitle={updateTitle} />


B.tsx
type Props = {
  ...
  handleTitleUpdate: (newTitle: string) => void;
  ...
};

const SidebarItem = ({
...
  handleTitleUpdate,
}: Props) => {
...
};

const handleTitleUpdate = useCallback(
    (editedTitle) => {
      updateTitle(editedTitle);
    },
    [updateTitle],
  );

第2引数であるupdateTitleが変化したときに、useCallbackによってメモ化された関数handleTitleUpdateが再生成されます。そして、そのhandleTitleUpdateが新しいタイトルを受け取って、updateTitleを経由してタイトルの状態が更新され、それがA.tsxで表示されます。

今回は表示したい場所がA.tsxにあり、B.tsxからhandleTitleUpdateをpropsとして渡す実装をしようとしていました。そのためuseCallbackで関数化して、propsとしてA.tsxに渡すことができました。handleTitleUpdate関数は高頻度で再生成されるのでuseCallbackを使用することでパフォーマンスが向上します。

終わりに

useEffectとuseCallbackは似ていますが、違いを理解しておけばパフォーマンスの高いアプリケーションを作ることができます。

アプリケーション実装中に、”useEffectを使用したけど、この実装はuseCallbackでもできるのでは?”と気づいた場面があったことが今回の記事を書くきっかけです。

今後も、似ている機能に出会った時にとにかくどちらかを使うのではなく、違いを調べてより適切な機能をつけていくように心がけたいです。


divxでは一緒に働ける仲間を募集しています。
興味があるかたはぜひ採用ページを御覧ください。

  採用情報 | 株式会社divx(ディブエックス) 可能性を広げる転職を。DIVXの採用情報ページです。 株式会社divx(ディブエックス)


 

お気軽にご相談ください


ご不明な点はお気軽に
お問い合わせください

サービス資料や
お役立ち資料はこちら

DIVXブログ

テックブログ タグ一覧

人気記事ランキング

GoTopイメージ