🐥
(React) useCallbackについてまとめてみました。
useCallback
React の Hook の一つです。
deps(依存配列)内の値が変わらない限り、同一の関数参照を返します。
const 関数名 = useCallback(
() => {
// 実行したい処理
},
[dep1, dep2, …] // これらの値が変わったときだけ新しく関数を作成
);
役割(スタンプ例)
- useCallback あり → いちど作った「スタンプ」をそのまま使って押せる
- useCallback なし → 押すたびにゴムから削って新しいスタンプを作り直すイメージ
いつ使う?
- React.memo した子コンポーネントへのハンドラを安定させたいとき
- useEffect の依存配列に関数を渡すとき
- カスタムフック(例: useInterval)でコールバックの一致を保ちたいとき
例:1. React.memo と組み合わせた子コンポーネントの最適化
import React, { useState, useCallback } from 'react';
const Child = React.memo(({ onClick }: { onClick: () => void }) => {
console.log('👶 Child がレンダーされました');
return <button onClick={onClick}>子ボタン</button>;
});
export function Parent() {
const [count, setCount] = useState(0);
// deps=[] → 初回のみ生成。以降は同じ関数参照を使い回す
const handleClick = useCallback(() => {
console.log('👉 子ボタンがクリックされました');
}, []);
return (
<div>
<p>Parent count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Parent +1</button>
<Child onClick={handleClick} />
</div>
);
}
挙動
-
初回レンダー時
👶 Child がレンダーされました
-
「Parent +1」クリック → Parent は再レンダーされるが
- Child は再レンダーされず(コンソール出力なし)
-
「子ボタン」クリック →
👉 子ボタンがクリックされました
例:2. useEffect の依存配列に関数を渡すパターン
import React, { useState, useCallback, useEffect } from 'react';
export function ExampleEffect() {
const [value, setValue] = useState(0);
// value が変わるたびに新しい compute が生成される
const compute = useCallback(() => {
console.log('🔄 compute を実行:', value);
}, [value]);
// compute が変わる(=value が変わる)たびに effect を実行
useEffect(() => {
compute();
}, [compute]);
return (
<button onClick={() => setValue(v => v + 1)}>
value: {value}
</button>
);
}
挙動
-
初回レンダー時
🔄 compute を実行: 0
-
「value」ボタンクリック → value が 1 に更新 →
🔄 compute を実行: 1
-
以降クリックする度に、その時点の value をログ出力
例:3. カスタムフック useInterval と組み合わせる例
import React, { useState, useCallback, useEffect, useRef } from 'react';
// useInterval: コールバックを ref で保持しつつ、delay ごとに呼び出す
function useInterval(callback: () => void, delay: number) {
const saved = useRef(callback);
useEffect(() => { saved.current = callback; }, [callback]);
useEffect(() => {
const id = setInterval(() => saved.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
export function Clock() {
const [time, setTime] = useState(() => new Date().toLocaleTimeString());
// deps=[] → tick の参照が変わらないので useInterval が安定動作
const tick = useCallback(() => {
setTime(new Date().toLocaleTimeString());
}, []);
useInterval(tick, 1000);
return <div>現在時刻: {time}</div>;
}
挙動
- 初回レンダー → 現在時刻を表示
-
以降 1 秒ごとに tick() が呼ばれ →
time
が更新 → UI が毎秒リアルタイムに変化
まだ使うべきタイミングを見極めるのは難しいですが、その特徴をしっかり理解した上で、経験を重ねながら適切に活用していきたいと思います。
Discussion