第84章:【フック】useCallback
1. この章のゴール 🎯
この章では、
- 「なんで**
useCallbackが出てくるのか?**」 - 「何をしてくれるフックなのか?」
- 「React 19 の世界で、どういう場面でまだ知っておくべきか?」
を、かわいく(?)サクッと理解することがゴールです ✨
キーワード: **「関数の“中身”じゃなくて“同じ人かどうか(参照)」を保つ」**っていう発想 👩🏫
2. 復習:関数を子コンポーネントに渡すときの問題 😵
前の章で出てきたキーワードをおさらいすると:
- React コンポーネントは 状態(state)や props が変わると再レンダー する
- 親が再レンダーすると、JS の世界では
() => { ... }みたいな**関数オブジェクトも全部「新しく作り直し」**される React.memoで子コンポーネントをメモ化しても、 props に渡している関数が毎回「別のオブジェクト」だと、子も再レンダーしちゃう
この流れを図でイメージしてみます 👀
mermaid
flowchart LR
A["親の state が変わる"] --> B["親コンポーネントが再レンダー"]
B --> C["JS の世界で<br/>毎回 新しい関数 onClick を作る"]
C --> D["子コンポーネントの props(onClick) の<br/>『参照』が毎回ちがう"]
D --> E["React.memo してても<br/>子コンポーネントが再レンダー 😢"]
ここで登場するのが useCallback です ✨
「この関数、中身が同じなら前のを再利用していいよ〜」と React にお願いするフックだと思ってください。(React)
3. useCallback の基本形 🧠
公式ドキュメントでの useCallback の型はこんなイメージです:
useCallback(fn, dependencies)
fn: メモ化したい関数dependencies: その関数の中で使っている “外側の値” の一覧(依存配列)(React)
TypeScript & React で書くと、いちばんシンプルな形はこんな感じです👇
tsx
import { useCallback, useState } from "react";
type ChildButtonProps = {
onClick: () => void;
};
function ChildButton({ onClick }: ChildButtonProps) {
console.log("ChildButton レンダー!");
return <button onClick={onClick}>子ボタンをクリック</button>;
}
export default function Parent() {
const [count, setCount] = useState(0);
// 👇 この関数を useCallback で「覚えておく」
const handleChildClick = useCallback(() => {
console.log("子ボタンが押されたよ!");
}, []); // ← 依存配列
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
<ChildButton onClick={handleChildClick} />
</div>
);
}
ここでのポイント ✍️
-
最初のレンダー時:
useCallbackに渡した関数がそのまま返ってくる
-
2回目以降のレンダー:
- 依存配列の中身が変わらなければ 「前に保存しておいた同じ関数オブジェクト」を返してくれる
-
[](空配列)の場合:- 「一度作ったら、基本ずっと同じ関数を使い回す」という意味
useCallback自体は Hooks の一種なので、他のフックと同じく コンポーネントのトップレベルでだけ呼び出す ルールがあります。(React)
4. React.memo とセットで使うときのイメージ 🔗
useCallback が本領発揮するのは、メモ化された子コンポーネントに関数を渡すときです。
tsx
import { memo, useCallback, useState } from "react";
type ChildButtonProps = {
onClick: () => void;
};
// 👇 子コンポーネントを React.memo でメモ化
const ChildButton = memo(function ChildButton({ onClick }: ChildButtonProps) {
console.log("ChildButton レンダー!");
return <button onClick={onClick}>子ボタンをクリック</button>;
});
export default function Parent() {
const [count, setCount] = useState(0);
const handleChildClick = useCallback(() => {
console.log("子ボタンが押されたよ!");
}, []);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
{/* 👇 onClick は「毎回同じ参照」なので、
count だけ変わった時は ChildButton は再レンダーされない */}
<ChildButton onClick={handleChildClick} />
</div>
);
}
この状態を図にすると、こんな感じです 🎨
mermaid
flowchart LR
A["親の state が変わる"] --> B["親コンポーネントが再レンダー"]
B --> C["useCallback により<br/>同じ onClick 関数を再利用"]
C --> D["子コンポーネントの props(onClick) の<br/>『参照』が変わらない"]
D --> E["React.memo な子は<br/>再レンダーしなくて OK 😊"]
5. 依存配列と TypeScript の関係 📚
useCallback の第2引数の 依存配列 は、かなり大事なポイントです。
tsx
const handleSave = useCallback(() => {
// ✅ ここで使っている値は、基本ぜんぶ依存配列に入れる
console.log("今の count:", count);
console.log("ユーザー名:", userName);
}, [count, userName]);
- 関数の中で使っている state / props / そのコンポーネント内で定義された変数や関数 は、 原則として 全部依存配列に書く イメージです
- Lint(ESLint + React Hooks ルール)を入れておくと、 「これ依存配列に書き忘れてない?」って教えてくれます 👮(React)
- TypeScript 的には、
useCallbackの中で引数の型をしっかり書いておくと安心です
tsx
const handleSubmit = useCallback((value: string) => {
console.log("送信:", value);
}, []);
↑みたいに書けば、value の型もちゃんとつきます(推論でもだいたいいけますが、明示してもOK)。
6. React 19 だとどうなるの?🤔
React 19 では、コンパイラによる自動最適化 がどんどん進んでいて、
- 「関数の参照をいい感じに安定させる」
- 「不要な再レンダーを自動で減らす」
といった最適化が入っている&入ろうとしています。(ShareWis Blog(シェアウィズ ブログ))
その結果、
- 昔ほど「なんでもかんでも
useCallback」って書かなくてよくなる - シンプルに
const handleClick = () => { ... }と書くだけで 十分なケースが増えてきています(DEV Community)
ただし、
-
useCallback自体は ちゃんと生きている公式フック で、- 既存の React 17 / 18 プロジェクト
- コンパイラをまだ導入していないプロジェクト
- ライブラリ側など、「絶対に関数の参照を固定したい」場面
-
では、今でもフツーに使われています 👌(GitHub)
この講座では: 「React 19 では前より出番が減るかもだけど、 **
useCallbackを理解しておくと、既存コードも怖くないし、 ‘なにが最適化されてるのか’ も見抜きやすくなるよ」 というスタンスで扱っていきます ✨
7. よくあるミス&チェックリスト ✅
useCallback でよくあるパターンを、先に知っておきましょう。
❌ よくあるミス
-
依存配列をわざと空にする
- 本当は
countを使ってるのに、[]にしてしまう → 「ずっと昔のcountを使い続けるバグ(古い値問題)」が起きる
- 本当は
-
「とりあえず全部
useCallbackにしちゃおう」と乱用useCallback自体にもオーバーヘッドがあります- 「本当にメモ化したいか?」を考えずに全部包むと、 逆にコードが読みにくく&遅くなることも 💦(DEV Community)
-
useCallbackを if 文の中で呼ぶ- Hooks のルール違反(コンポーネントのトップレベルで呼ぶこと)
✅ ざっくりチェックリスト
useCallback を検討してもよさそうな場面:
- 子コンポーネントを
React.memoでメモ化している - その子に渡している関数が原因で「ムダな再レンダー」が多そう
- 子コンポーネントが 重い描画(グラフ・大きなリストなど) をしている
そこまで気にしなくていい場面:
- 画面が小さくて、子コンポーネントも軽い
- パフォーマンス的に困っていない
- React 19 + Compiler で、特にカクつきを感じない
※ メモ化や最適化についての「全体の考え方」は、第90章で改めて整理します 🌈
8. まとめ&次章へのつなぎ 🚀
この章で覚えておきたいポイントはこれだけです 💡
useCallback(fn, deps)は、関数の「参照」自体を覚えておくフックReact.memoな子コンポーネントに関数を渡すとき、 子のムダな再レンダーを減らすために使うことが多い- 依存配列には、関数の中で使う外側の値(props / state など)をちゃんと入れる
- React 19 では自動最適化も進んでいるけど、
useCallbackの考え方を知っておくこと自体がすごく大事
次の 第85章 では、
「
React.memoとuseCallbackを組み合わせて、 実際に子コンポーネントのムダな再レンダーを止めてみる」
という 実践編 を一緒にやっていきます 💻✨ コンソールにログを出しながら、「ほんとに再レンダー減ってる!」を体感してみよう〜 😆