• システム開発に関わる内容をざっくりと書いていく

useCallback / useMemo (メモ化)のキャッチアップ

useCallback / useMemo:無駄な再レンダリングを減らす武器たち

Reactアプリが大きくなってくると気になってくるのが「再レンダリングの無駄」。
useCallbackuseMemoは、処理の無駄を減らすための最適化フック。ただし、やみくもに使うと逆に読みづらくなるので、使いどころがわかってると強い武器になる。


まず前提:なぜ再レンダリングが問題なのか?

Reactは「状態が変わると、そのコンポーネントと子孫が再レンダリングされる」仕組み。
このときpropsとして渡す関数や値が毎回新しく生成されると、子コンポーネントも無駄に再描画されることがある。


🧠 useCallback:関数をメモ化して再生成を防ぐ

基本形

tsx
const handleDelete = useCallback((id: number) => {
setTodos((prev) => prev.filter((todo) => todo.id !== id));
}, []);

何が起きてる?

  • 通常、handleDeleteは毎回再生成される(=参照が毎回変わる)
  • それをuseCallbackでラップすると、依存が変わらない限り同じ参照を再利用できる
  • React.memoと一緒に使うと特に効果的

よくあるパターン

tsx
<TodoItem todo={todo} onDelete={handleDelete} />
  • 子コンポーネントがmemo()でメモ化されているなら、onDeleteの参照が変わらなければ再レンダリングされない
  • useCallbackがないと、親が再レンダリングされるたびに子も巻き込まれる

🧮 useMemo:重たい計算の結果をメモ化

基本形

tsx
const todoCount = useMemo(() => {
console.log("Calculating todo count...");
return todos.length;
}, [todos]);

何が起きてる?

  • 通常、毎回todos.lengthが再計算される
  • useMemoを使えば、todosが変わらない限り前回の計算結果を再利用できる
  • console.logで確認すると、無駄な計算が減っているのがわかる

useMemoとuseCallbackの違い

フック対象目的
useCallback関数同じ関数を再利用
useMemo計算結果計算結果を再利用

✋ React.memoと一緒に使うと効果倍増

tsx
const TodoItem = memo(({ todo, onDelete }) => {
console.log(`rendered: ${todo.text}`);
return <div>{todo.text}</div>;
});
  • memoで子コンポーネントをpropsが変わらない限り再レンダリングしないようにできる
  • useCallbackuseMemopropsの参照を変えないようにすれば、子の再レンダリングを防げる

✅ いつ使うべき?

  • 同じ関数が毎回生成されて、子コンポーネントが無駄に再描画されてるとき
  • 重たい計算処理を毎回やりたくないとき(配列のフィルタ・ソート・集計など)

❌ いつ使うべきじゃない?

  • 処理が軽いのにとりあえずメモ化する場合 → 逆に複雑になって読みづらくなる
  • パフォーマンスに影響がない単純UI → 無意味な最適化になる

デバッグのコツ

  • console.log("rendered")"Calculating..."を仕込んで様子を見る
  • React DevToolsで再レンダリングの状況を確認する
  • メモ化の効果がなければ、無理に使わない

👇 サンプルアプリ:useCallback/useMemo 学習アプリ


まとめ

  • useCallback関数をメモ化して子コンポーネントへの無駄な再レンダリングを防ぐ
  • useMemo重たい処理の結果をメモ化して毎回再計算を防ぐ
  • React.memoと組み合わせると効果が見える
  • 適材適所で使えば、パフォーマンスもコードの読みやすさも両立できる