はじめに
フックAPIリファレンスでuseCallbackの存在は知っていましたが、実際に使ったことがなかったので、使い所を調べて使ってみた話をします。
useCallbackとは
メモ化してくれる関数らしいです。
なんだかパフォーマンスを上げてくれそうな気がしますね。
Todoアプリを作りながら、試してみましょう。
Todoアプリのサンプルを作成
とりあえず、簡単なTodoアプリを作成しました。
ファイル構成はこんな感じです。
-
src/Todos/index.tsx- Todo追加フォームと、TodoListコンポーネントをレンダリングしている
- TodoListコンポーネントには、
todosデータを渡している。
-
src/TodoList/index.tsx- 受け取った
todosデータをループで表示 - 1件1件の
todoデータは、Todoコンポーネントに渡してレンダリングしている
- 受け取った
-
src/Todo/index.tsx- 受け取った
todoデータを表示 - 削除ボタンを表示
- 受け取った
画面の見た目はこんな感じです。
パフォーマンスの問題点を探す
無駄にレンダリングされている場所を探します。
React Developer Tools拡張機能のHighlight updates when components render.で見ても良いですが、TodoListの中でconsole.logが何回出ているかで確認してみました。
すると、Todo追加用の入力フォームに、文字を入力するたびにTodoListコンポーネントがレンダリングされていることが分かりました。
<TodoList todos={todos} />
こんな感じで、TodoListコンポーネントをレンダリングしていますが、todosしか渡していません。
入力値が更新されても、todosの値は変わらないので、無駄にレンダリングされていることが分かります。
React.memoを使う
一旦本題とは別の話ですが、React.memoは、渡されたpropsの変更のみをチェックし、変更があれば、再レンダリングされます。
-import React from "react";
+import React, { memo } from "react";
-export default TodoList;
+export default memo(TodoList);
無駄なレンダリングは解消されました。
削除ボタンが機能していないので、使えるようにする
React.memoの話をしておく都合上削除ボタンに削除用のメソッドを渡していませんでした。
TodoListコンポーネントのレンダリングでは、todosデータ以外に、削除用関数を渡すような書き方に変えます。
<TodoList todos={todos} delTodo={delTodo} />
TodoListが無駄にレンダリングされるようになってしまった。。。
delTodo関数を渡すようにしただけで、またTodoListコンポーネントが無駄にレンダリングされるようになってしまいました。
どうやら、Todosコンポーネントが再レンダリングされるたび(入力フォームに変更があるたび)に、delTodoの関数が作り直され、別物扱いとなり、TodoListコンポーネント側では、新しく作り直されたdelTodoメソッドを受け取るので、変更されたと判断され、再度、レンダリングしているようです。
useCallbackを使う
やっと本題です。
delTodoメソッドをメモ化して、新しく作られないようにします。
-<TodoList todos={todos} delTodo={delTodo} />
+<TodoList todos={todos} delTodo={useCallback(delTodo, [])} />
TodoListコンポーネントに渡しているdelTodoメソッドがメモ化され、同じものが使われるようになり、無駄なレンダリングが発生しないようになりました。
delTodoメソッドを常に同じものを使うようにメモ化しましたが、状況によっては、作り直して欲しい場合もあるかと思います。
その際は、useCallbackの第2引数の配列を使います。
例えば、hogeという変数が変わった時に、delTodoメソッドも作り直して欲しい場合、useCallback(delTodo, [hoge])となります。
おわりに
ほとんど参考にしたサイトのままとなってしまいましたが、自分の体験した使い所をご紹介しました。




