React 入門:useCallbackで関数を最適化する

React ではコンポーネントが再レンダーされるたびに、関数も毎回再生成されます。小規模なアプリでは問題にならないこともありますが、レンダーが多くなるコンポーネントや子コンポーネントに関数を渡す場合、無駄な再レンダーが発生してパフォーマンスに影響することがあります。

そんなときに役立つのが useCallback フック です。


useCallback とは?

useCallback関数をメモ化(再生成を防ぐ) するためのフックです。
関数を依存配列の値が変わったときだけ再生成するように制御できます。

import { useCallback } from "react";

const memoizedFunction = useCallback(() => {
  console.log("関数が呼ばれました");
}, []); // 依存配列が空なら初回のみ生成

いつ使うべき?

  • 子コンポーネントに関数を渡すとき
    関数が毎回新しく生成されると、子コンポーネントも再レンダーされてしまいます。

  • レンダーが多いコンポーネントで重い処理を行うとき
    無駄な再生成を防ぐことでパフォーマンスを向上できます。


基本的な使い方

親から子へ関数を渡す場合

import { useState, useCallback } from "react";

function Child({ onClick }) {
  console.log("Childレンダー");
  return <button onClick={onClick}>クリック</button>;
}

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

  const handleClick = useCallback(() => {
    console.log("ボタンがクリックされました");
  }, []); // 依存がなければ初回のみ生成

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>1</button>
      <Child onClick={handleClick} />
    </div>
  );
}
  • handleClickuseCallback によって初回のみ生成されます
  • 親コンポーネントが再レンダーされても、子コンポーネントは無駄に再レンダーされません

useCallback の注意点

  1. 必要ないところで使うと逆に複雑化

    • 小規模アプリや軽量な関数では使わない方がシンプル
  2. 依存配列の指定を忘れない

    • 依存配列に含める変数が変わったときに関数を再生成する
const handleClick = useCallback(() => {
  console.log(count); // countを依存に入れ忘れると古い値を参照
}, [count]);
  1. useMemo と似ているが用途が違う

    • useCallback → 関数の再生成を防ぐ
    • useMemo → 値(計算結果)の再生成を防ぐ

実践例:Todo リストで関数を渡す

import { useState, useCallback } from "react";

function TodoItem({ todo, onDelete }) {
  console.log(`${todo}レンダー`);
  return (
    <li>
      {todo} <button onClick={onDelete}>削除</button>
    </li>
  );
}

function TodoList() {
  const [todos, setTodos] = useState(["勉強", "掃除", "買い物"]);

  const handleDelete = useCallback((index) => {
    setTodos((prev) => prev.filter((_, i) => i !== index));
  }, []);

  return (
    <ul>
      {todos.map((todo, index) => (
        <TodoItem
          key={index}
          todo={todo}
          onDelete={() => handleDelete(index)}
        />
      ))}
    </ul>
  );
}
  • handleDeleteuseCallback でメモ化
  • 不要な再レンダーを防ぎ、パフォーマンスを最適化

まとめ

  • useCallback関数をメモ化して再生成を防ぐフック
  • 子コンポーネントへの関数渡しやレンダーが多い場面で有効
  • 小規模な関数には使いすぎない
  • useMemo との違いを理解して使い分ける