はじめに
Reactを書いていると、「このコンポーネント、何回も無駄に動いてない?」って気になることありませんか?
それ、**useCallback
**を使えば解決できます!
この記事では、コード例を交えながら「どう使うのか」「なぜ便利なのか」を、初心者の方にも分かりやすく説明します。
useCallbackってなに?
useCallback
は関数を再利用するためのReactフックです。
Reactのコンポーネントは、状態が変わるたびに全部再描画されるので、
普通に関数を定義すると、何度も新しい関数が作られちゃいます。
でも、useCallback
を使うと、必要なときだけ関数を更新して、
それ以外は「前に作ったやつそのまま使おう!」ってできます。
基本構文
const memoizedCallback = useCallback(
() => {
// コールバック関数の内容
},
[依存関係]
);
- 第一引数: メモ化したい関数。
- 第二引数: 関数を再生成する条件となる依存関係の配列。
useCallbackを使う理由
再レンダリングを防ぐ
通常、Reactの関数コンポーネントでは毎回新しい関数が作られます。例えば以下のようなコードを考えてみましょう。
const Child = React.memo(({ onClick }) => {
console.log("Child is rendering!");
return <button onClick={onClick}>Click me</button>;
});
function App() {
const [count, setCount] = React.useState(0);
const handleClick = () => {
console.log("Button clicked");
};
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<Child onClick={handleClick} />
</div>
);
}
この場合、App
が再レンダリングされるたびに新しいhandleClick
関数が生成されるため、Child
も再レンダリングされてしまいます。ここでuseCallback
を使うとどうなるでしょう?
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []);
useCallback
を使うことで、handleClick
は常に同じ関数オブジェクトを保持し、React.memo
と組み合わせることでChild
の再レンダリングを防ぐことができます。
React.memoとの連携
React.memo
はコンポーネントをメモ化して、props
が変更されない限り再レンダリングを防ぎます。しかし、渡されるprops
が新しいオブジェクトや関数である場合、React.memo
の効果が消えてしまいます。
React.memoを組み合わせると…。
const Child = React.memo(({ onClick }) => {
console.log("Child is rendering!");
return <button onClick={onClick}>Click me</button>;
});
function App() {
const [count, setCount] = React.useState(0);
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<Child onClick={handleClick} />
</div>
);
}
これにより、handleClick
が常に同じオブジェクトを保持し、React.memo
が正常に動作します。
第二引数の重要性
useCallback
の第二引数(依存関係の配列)は、関数が再生成されるタイミングを決定します。この依存関係に基づいて、必要な場合だけ関数を新しく生成します。
依存関係が空の場合
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []);
- 効果:
handleClick
は一度だけ生成され、それ以降再生成されません。 - 適用例: 外部の状態や値を参照しない関数。
依存関係がある場合
const handleClick = useCallback(() => {
console.log("Count is: ", count);
}, [count]);
- 効果:
count
が変更されたときのみ、新しい関数が生成されます。 - 適用例: 関数内で
count
などの外部の値を参照する場合。
依存関係が適切に設定されていないと、最新の値を参照できなかったり、不要なレンダリングが発生する可能性があります。
関数を渡すときの注意点
useCallback
を使うべき場面とそうでない場面があります。
使うべきケース
- 子コンポーネントに関数を
props
として渡す場合。 - 再レンダリングが頻繁に発生する場面で、パフォーマンスを最適化したい場合。
過剰に使わない
useCallback
を使いすぎるとコードが複雑になり、逆にメモ化コストでパフォーマンスが低下することもあります。
実用例:フィルタリング機能
以下は、useCallback
を使った実際のアプリケーションの例です。
function App() {
const [items, setItems] = React.useState([1, 2, 3, 4, 5]);
const [filter, setFilter] = React.useState("");
const filteredItems = React.useMemo(() => {
return items.filter(item => item.toString().includes(filter));
}, [items, filter]);
const handleChange = useCallback((event) => {
setFilter(event.target.value);
}, []);
return (
<div>
<input onChange={handleChange} placeholder="Filter items" />
<ul>
{filteredItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
まとめ
useCallback
は、Reactアプリケーションのパフォーマンスを向上させるための重要なツールです。特に、React.memo
と組み合わせることで、子コンポーネントの不要な再レンダリングを防ぐ効果があります。
- React.memo: 子コンポーネントのメモ化。
- useCallback: 関数のメモ化。
- 第二引数: 関数の再生成をコントロール。
公式ドキュメントも参考にしてください!
- URL: useCallback – React公式
- URL: React.memo – React公式