カスタムフックとは
- カスタムフックは、Reactの再利用可能なロジックを作るための仕組み
- 通常のReactフック(
useState
、useEffect
など)を組み合わせて、新しいフックを自分で作成する
なぜカスタムフックを使うか
コードの再利用性を向上
- 例えば、複数のコンポーネントで同じロジック(API呼び出し、フォームのバリデーションなど)を使いたい場合、カスタムフックにそのロジックをまとめることで、簡単に再利用できる
コードの読みやすさを向上
- ロジックをフックに分けてコンポーネントから切り離すことで、コンポーネントのコードがスッキリする
状態管理や副作用を効率的に扱う
- 状態(
state
)や副作用(effect
)をカスタムフックに閉じ込めることで、分離したロジックをシンプルにテストできる
使い方
定義
カスタムフックの名前は必ず「use
」で始めます。これはReactがフックとして認識するためのルールです。
// ビジネスロジックはカスタムフックに切り出して管理する
export const useTodoList = () => {
const [todoList, setTodoList] = useState<Todo[]>([]);
// マウント時に1度だけ、ローカルストレージからデータを取得
useEffect(() => {
const todoListData = localStorage.getItem("todo-list");
if (todoListData) {
setTodoList(JSON.parse(todoListData));
}
}, []);
// todoListが更新されるたびに、ローカルストレージに保存
useEffect(() => {
localStorage.setItem("todo-list", JSON.stringify(todoList));
}, [todoList]);
// 新しいTODOを追加する関数
const addTodo = (newTask: string, newPerson: string, newDeadline: string) => {
setTodoList((prev: Todo[]) => [
...prev,
{
id: Date.now(),
task: newTask,
person: newPerson,
deadline: newDeadline,
},
]);
};
// TODOを削除する関数
const handleDelete = (id: number) => {
setTodoList((prev) => prev.filter((todo) => todo.id !== id));
};
return { todoList, addTodo, handleDelete };
};
呼び出し
// useTodoList()を呼び出す
// todoList, addTodo, handleDelete が呼び出し先のコンポーネントで使用することができる。
const { todoList, addTodo, handleDelete } = useTodoList();
return (
// TodoListの孫コンポーネントでuseTodoList()を使うためにバケツリレーで繋ぐ
<TodoList todoList={todoList} handleDelete={handleDelete}></TodoList>
);
コンポーネント呼び出しのバケツリレーの違い
バケツリレーあり
先ほどの useTodoList()
は、孫コンポーネントで使用するためにバケツリレーでpropsを渡した。 これは、App.tsx
で呼び出した useTodoList();
と同期した状態で使用するため。
このような場合、親 → 子 → 孫 とバケツリレーでpropsを渡す必要がある。
たとえば以下のようなイメージで複数のページがあり、それぞれが別の useTodoList()
として切り離す場合などはバケツリレーが不要になる
- yamadaApp.tsx
const { todoList, addTodo, handleDelete } = useTodoList();
- 山田用の
useTodoList();
- tanakaApp.tsx
const { todoList, addTodo, handleDelete } = useTodoList();
- 田中用の
useTodoList();
- satoApp.tsx
const { todoList, addTodo, handleDelete } = useTodoList();
- 佐藤用の
useTodoList();
バケツリレーなし
例えば、ボタンをクリックしたら一秒ずつ加算するタイマーのようなカスタムフックがあった場合
それぞれのカウント秒数を切り離す場合には、propsのバケツリレーは不要になる。
import { useEffect, useState } from "react";
export const useCountUpTimer = () => {
const [time, setTime] = useState(0);
useEffect(() => {
const timer = setInterval(() => setTime((prev: number) => prev + 1), 1000);
return () => {
clearInterval(timer);
};
}, []);
return { time };
};
export const App = () => {
// 子コンポーネントのタイマーとは別のタイマー
const { time } = useCountUpTimer();
return (
<main>
<div>タイマー:{time}</div>
<TodoItem></TodoList>
</main>
);
};
export const TodoItem = () => {
// 親コンポーネントのタイマーとは別のタイマー
const { time } = useCountUpTimer();
return (
<div>タイマー:{time}</div>
);
};
復習内容のメモ
バケツリレーの概念← 今回でかなり理解できた- カスタムフックの基本の書き方を復習する。まだ理解が浅い部分が多い
useHogeHoge
の名前にする- 値をオブジェクト型
{ hoge }
で返す
リポジトリ
感想
カスタムフックについて学んできました。
今まで、コンポーネントの中に、ロジックが書いてあり気持ち悪い部分がありましたが、以下のように切り出せることを学習できました。
- コンポーネントにはJSXのコードのみ書く
- ビジネスロジックは別に切り出して書く
ただ、カスタムフックに切り出さない方が可読性が良いケースもおおくあるでしょうから、この辺のお左方はやりながらキャッチアップするしかないかなと思っています。