TECH I.S.

React useCallbackフック


React useCallbackフックはメモ化されたコールバック関数を返します。

メモ化は、再計算する必要がないように値をキャッシュすることと考えてください。

これにより、リソースを集中的に使用する関数を分離して、すべてのレンダリングで自動的に実行されないようにすることができます。

フックuseCallbackは、依存関係の1つが更新された場合にのみ実行されます。

これにより、パフォーマンスが向上します。

useCallbackそしてuseMemoフックは似ていますが、主な違いは次のとおりです。useMemoメモ化されたものを返します*価値*そしてuseCallbackメモ化されたものを返します*関数*useMemo について詳しくは、useMemo をご覧ください。


問題

使用する理由の1つはuseCallback、プロパティが変更されない限りコンポーネントが再レンダリングされないようにするためです。

この例では、Todoが変更されない限りTodos コンポーネントは再レンダリングされないと考えるかもしれません。

これは、React.メモセクションの例と似ています。

例:

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プロパティが変更された場合にのみ再レンダリングされるようになりました。

プログラミング学習を加速させる

プログラミングをプロの講師に教えてもらいませんか。

テックアイエスのプログラミングスクールは初心者も大歓迎です。年齢制限もありません。転職・副業に強く、挫折させない手厚いサポートで稼ぐ力を身につけましょう!

スクールの詳細