【デイトラ学習記録】DAY33 カスタムフック

カスタムフックとは

  • カスタムフックは、Reactの再利用可能なロジックを作るための仕組み
  • 通常のReactフック(useStateuseEffect など)を組み合わせて、新しいフックを自分で作成する

なぜカスタムフックを使う

コードの再利用性を向上

  • 例えば、複数のコンポーネントで同じロジック(API呼び出し、フォームのバリデーションなど)を使いたい場合、カスタムフックにそのロジックをまとめることで、簡単に再利用できる

コードの読みやすさを向上

  • ロジックをフックに分けてコンポーネントから切り離すことで、コンポーネントのコードがスッキリする

状態管理や副作用を効率的に扱う

  • 状態(state)や副作用(effect)をカスタムフックに閉じ込めることで、分離したロジックをシンプルにテストできる

使い方

定義

カスタムフックの名前は必ず「use」で始めます。これはReactがフックとして認識するためのルールです。

src/js/component/todo/use-todo-list.ts
// ビジネスロジックはカスタムフックに切り出して管理する


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 };
};

呼び出し

src/js/App.tsx
// 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のバケツリレーは不要になる。

src/js/component/custom-hook/use-count-up-timer.tsx
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 };
};
src/js/App.tsx
export const App = () => {
  // 子コンポーネントのタイマーとは別のタイマー
  const { time } = useCountUpTimer();

  return (
    <main>
      <div>タイマー:{time}</div>
      <TodoItem></TodoList>
    </main>
  );
};
src/js/App.tsx
export const TodoItem = () => {
  // 親コンポーネントのタイマーとは別のタイマー
  const { time } = useCountUpTimer();

  return (
    <div>タイマー:{time}</div>
  );
};

復習内容のメモ

  • バケツリレーの概念 ← 今回でかなり理解できた
  • カスタムフックの基本の書き方を復習する。まだ理解が浅い部分が多い
    • useHogeHogeの名前にする
    • 値をオブジェクト型 { hoge } で返す

リポジトリ

感想

カスタムフックについて学んできました。

今まで、コンポーネントの中に、ロジックが書いてあり気持ち悪い部分がありましたが、以下のように切り出せることを学習できました。

  • コンポーネントにはJSXのコードのみ書く
  • ビジネスロジックは別に切り出して書く

ただ、カスタムフックに切り出さない方が可読性が良いケースもおおくあるでしょうから、この辺のお左方はやりながらキャッチアップするしかないかなと思っています。

Yuta | Code.Yu

WordPressをメインに活動する、フリーランスのWeb制作コーダーです。
React案件を経験したことをきっかけに、さらにフロントエンド開発のスキルを高めるため、JavaScriptやReactの学習を進めています。このブログでは、学習の過程や記録を発信しています。

Web制作に関する情報はこちら
Code.Yu | ホームページ制作・コーディング代行 ↗︎