React useCallbackフック
はメモ化されたコールバック関数を返します。
メモ化は、再計算する必要がないように値をキャッシュすることと考えてください。
これにより、リソースを集中的に使用する関数を分離して、すべてのレンダリングで自動的に実行されないようにすることができます。
フックuseCallback
は、依存関係の1つが更新された場合にのみ実行されます。
これにより、パフォーマンスが向上します。
のuseCallback
そしてuseMemo
フックは似ていますが、主な違いは次のとおりです。useMemo
メモ化されたものを返します*価値*そしてuseCallback
メモ化されたものを返します*関数*useMemo について詳しくは、useMemo をご覧ください。章。
問題
使用する理由の1つはuseCallback
、プロパティが変更されない限りコンポーネントが再レンダリングされないようにするためです。
この例では、Todo
が変更されない限りTodos
コンポーネントは再レンダリングされないと考えるかもしれません。
例:
index.js
import { useState } from "react";
import ReactDOM from "react-dom/client";
import Todos from "./Todos";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const increment = () => {
setCount((c) => c + 1);
};
const addTodo = () => {
setTodos((t) => [...t, "New Todo"]);
};
return (
<>
<Todos todos={todos} addTodo={addTodo} />
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
</div>
</>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
Todos.js
import { memo } from "react";
const Todos = ({ todos, addTodo }) => {
console.log("child render");
return (
<>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
<button onClick={addTodo}>Add Todo</button>
</>
);
};
export default memo(Todos);
実行例(開発準備中) »
これを実行してみて、カウントインクリメントボタンをクリックしてください。
Todo
が変更されない場合でも、Todo
コンポーネントが再レンダリングされることがわかります。
なぜこれが機能しないのでしょうか?メモ
を使用しているため、カウントが増加してもtodos
状態もaddTodo
関数も変化しないため、Todos
コンポーネントは再レンダリングされません。
これは、「参照等価性」と呼ばれるものによるものです。
コンポーネントが再レンダリングされるたびに、その関数が再作成されます。このため、addTodo
機能が実際に変更されました。
解決
これを修正するには、useCallback
フックを使用して、必要でない限り関数が再作成されないようにします。
useCallback
フックを使用して、Todos
コンポーネントが不必要に再レンダリングされないようにします。
例:
index.js
import { useState, useCallback } from "react";
import ReactDOM from "react-dom/client";
import Todos from "./Todos";
const App = () => {
const [count, setCount] = useState(0);
const [todos, setTodos] = useState([]);
const increment = () => {
setCount((c) => c + 1);
};
const addTodo = useCallback(() => {
setTodos((t) => [...t, "New Todo"]);
}, [todos]);
return (
<>
<Todos todos={todos} addTodo={addTodo} />
<hr />
<div>
Count: {count}
<button onClick={increment}>+</button>
</div>
</>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
Todos.js
import { memo } from "react";
const Todos = ({ todos, addTodo }) => {
console.log("child render");
return (
<>
<h2>My Todos</h2>
{todos.map((todo, index) => {
return <p key={index}>{todo}</p>;
})}
<button onClick={addTodo}>Add Todo</button>
</>
);
};
export default memo(Todos);
実行例(開発準備中) »
Todos
コンポーネントは、todos
プロパティが変更された場合にのみ再レンダリングされるようになりました。