[React.js] - パート14: React Hooks - useCallback Hook

Ace Lennox
ReactのuseCallback
フックは、コールバック関数をメモ化することでパフォーマンスを向上させます。メモ化とは、値をキャッシュして再計算を不要にする仕組みのことです。このガイドでは、useCallback
の使い方を簡単な例を使ってわかりやすく解説します。
useCallback
とは?
useCallback
フックは、メモ化されたコールバック関数を返します。この関数は、依存関係が変更された場合にのみ再生成されます。特に、関数をプロパティとして渡すコンポーネントでReact.memo
と組み合わせて使用することで、再レンダリングを防ぐのに役立ちます。
主なポイント
関数のメモ化: 不要な再生成を防ぎます。
パフォーマンス最適化: 子コンポーネントの再レンダリングを減らします。
useMemo
との違い:useMemo
はメモ化された値を返し、useCallback
はメモ化された関数を返します。
問題:不要な再レンダリング
親コンポーネントでタスクのリストを管理するアプリを作成しましょう。
例:useCallback
なし
index.js
import { useState } from "react";
import ReactDOM from "react-dom/client";
import Tasks from "./Tasks";
const App = () => {
const [count, setCount] = useState(0);
const [tasks, setTasks] = useState([]);
const increment = () => {
setCount((c) => c + 1);
};
const addTask = () => {
setTasks((prevTasks) => [...prevTasks, `Task ${prevTasks.length + 1}`]);
};
return (
<>
<Tasks tasks={tasks} addTask={addTask} />
<hr />
<div>
Count: {count}
<button onClick={increment}>Increment Count</button>
</div>
</>
);
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
Tasks.js
import { memo } from "react";
const Tasks = ({ tasks, addTask }) => {
console.log("Tasks component re-rendered!");
return (
<div>
<h2>Tasks</h2>
{tasks.map((task, index) => (
<p key={index}>{task}</p>
))}
<button onClick={addTask}>Add Task</button>
</div>
);
};
export default memo(Tasks);
問題:Tasks
が再レンダリングされる理由
React.memo
を使用しても、カウントが増加するたびにTasks
コンポーネントが再レンダリングされます。これは、親コンポーネントが再レンダリングされるたびにaddTask
関数が再生成され、参照の等価性が崩れるためです。
解決策:useCallback
の使用
useCallback
フックを使うと、依存関係が変更されない限り関数を再生成しないようにできます。
例:useCallback
を使用
index.js
import { useState, useCallback } from "react";
import ReactDOM from "react-dom/client";
import Tasks from "./Tasks";
const App = () => {
const [count, setCount] = useState(0);
const [tasks, setTasks] = useState([]);
const increment = () => {
setCount((c) => c + 1);
};
const addTask = useCallback(() => {
setTasks((prevTasks) => [...prevTasks, `Task ${prevTasks.length + 1}`]);
}, [tasks]);
return (
<>
<Tasks tasks={tasks} addTask={addTask} />
<hr />
<div>
Count: {count}
<button onClick={increment}>Increment Count</button>
</div>
</>
);
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
Tasks.js
import { memo } from "react";
const Tasks = ({ tasks, addTask }) => {
console.log("Tasks component re-rendered!");
return (
<div>
<h2>Tasks</h2>
{tasks.map((task, index) => (
<p key={index}>{task}</p>
))}
<button onClick={addTask}>Add Task</button>
</div>
);
};
export default memo(Tasks);
なぜこれで動作するのか?
useCallback
がaddTask
関数をメモ化します。Tasks
コンポーネントは、tasks
プロパティが変更された場合のみ再レンダリングされます。
ベストプラクティス
React.memo
と組み合わせて使用: 子コンポーネントの再レンダリングを防ぐために、useCallback
をReact.memo
と一緒に使用します。依存関係を明確に: コールバック関数内で使用されるすべての変数を依存配列に含めることを忘れないでください。
必要な場合にのみ最適化:
useCallback
を多用しすぎないでください。特にパフォーマンスに影響する場面で使用しましょう。
まとめ
useCallback
フックは、Reactアプリケーションのパフォーマンスを向上させるためのシンプルで効果的なツールです。関数を必要に応じて再生成し、不要な再レンダリングを減らすことで、効率的なアプリケーションを構築できます。
この記事の例を試して、useCallback
を活用してReactアプリケーションをより効率的にしてみてください!
ソースコード
このプロジェクトの完全なソースコードはGitHubで利用可能です。