[React]カスタムフックについて

# カスタムフックとは
Reactのカスタムフックは、特定の機能やロジックを分離して再利用可能にすることで、コンポーネントコードの整理に非常に役立ちます。
本記事では、APIからデータを取得するためのカスタムフックを作成し、効率的なデータ取得と表示を実現する方法を紹介します。

# カスタムフックの基本概念
Reactのカスタムフックは、特定の処理を他の関数やコンポーネントと分離し、コードの再利用性を高めるための関数です。useStateやuseEffectといったReactのフックを利用することで、カスタムフックは簡単にステートや副作用を管理できます。

たとえば、データ取得のロジックをカスタムフックに移動させると、異なるコンポーネントで同じ取得ロジックを使い回せるようになります。以下は、APIからデータを取得し、その結果やローディング状態、エラーメッセージを管理するカスタムフック useFetchData の例です。

# 実装例
以下のuseFetchDataは、任意のURLからデータを取得し、コンポーネントに返すための基本的なカスタムフックです。このフックは、データ、ローディング状態、エラーメッセージを含むオブジェクトを返します。

``` ts
import { useState, useEffect } from "react";

interface FetchDataResult<T> {
  data: T | null;
  isLoading: boolean;
  error: Error | null;
}

export function useFetchData<T>(url: string): FetchDataResult<T> {
  const [data, setData] = useState<T | null>(null);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error("Failed to fetch data");
        const result: T = await response.json();
        setData(result);
      } catch (error) {
        setError(error as Error);
      } finally {
        setIsLoading(false);
      }
    };
    fetchData();
  }, [url]);

  return { data, isLoading, error };
}
```

# useFetchTasksの実装
```ts
import { useState, useEffect, useCallback } from "react";
import {
  fetchTasks,
  fetchCompletedTasks,
  fetchUnCompletedTasks,
  fetchInProgressTasks,
} from "@/utils/task-actions";
import { TaskType } from "@/types/task-type";

interface UseFetchTasksResult {
  tasks: TaskType[];
  isLoading: boolean;
  getTasks: () => Promise<void>;
}

export const useFetchTasks = (taskType: string): UseFetchTasksResult => {
  const [tasks, setTasks] = useState<TaskType[]>([]);
  const [isLoading, setIsLoading] = useState<boolean>(true);

  const fetchTaskByType = useCallback(async (type: string): Promise<TaskType[]> => {
    switch (type) {
      case "completed":
        return fetchCompletedTasks();
      case "unCompleted":
        return fetchUnCompletedTasks();
      case "inProgress":
        return fetchInProgressTasks();
      default:
        return fetchTasks();
    }
  }, []);

  const getTasks = useCallback(async () => {
    setIsLoading(true);
    try {
      const fetchedTasks = await fetchTaskByType(taskType);
      setTasks(fetchedTasks);
    } catch (error) {
      console.error("Failed to fetch tasks:", error);
    } finally {
      setIsLoading(false);
    }
  }, [taskType, fetchTaskByType]);

  useEffect(() => {
    getTasks();
  }, [getTasks]);

  return { tasks, isLoading, getTasks };
};

```
## useFetchTasksの動作
taskTypeを切り替えるたびに、フックが自動的に新しいタスクリストを取得しtasksに設定します。
isLoadingでローディング状態を管理し、取得が完了すると一覧が表示されるようにします。

## useFetchTasks を使ったコンポーネントの実装
最後に、useFetchTasks を使ってデータを取得し、表示するコンポーネントを作成します。ボタンでタスクの状態を切り替えられるようにしてみましょう。

```ts
import { useState } from "react";
import { useFetchTasks } from "@/hooks/useFetchTasks";
import { TaskRow } from "@/components/task-row";
import { Loading } from "@/components/ui/loading";

export default function TaskList() {
  const [taskType, setTaskType] = useState<string>("unCompleted");
  const { tasks, isLoading, getTasks } = useFetchTasks(taskType);

  return (
    <div>
      <button onClick={() => setTaskType("completed")}>Completed Tasks</button>
      <button onClick={() => setTaskType("unCompleted")}>Uncompleted Tasks</button>
      <button onClick={() => setTaskType("inProgress")}>In Progress Tasks</button>

      {isLoading ? (
        <Loading />
      ) : (
        tasks.map((task) => <TaskRow key={task.id} task={task} />)
      )}
    </div>
  );
}
```
このコンポーネントは、以下のように動作します。

・taskTypeを変更するボタンがあり、それぞれのボタンをクリックすると、異なるタスクリストが取得されます。
・isLoadingを利用して、データ取得中にはローディングインジケーターを表示し、取得後にタスク一覧を表示します。


# まとめ
カスタムフックを使うことで、ロジックをコンポーネントから分離し、再利用可能なコードとして保つことができます。
特にデータ取得のような一般的な処理は、カスタムフックにまとめておくことで、コードの見通しがよくなり、保守性も向上します。
Reactアプリケーションでデータ取得をシンプルかつ効率的に実装したい場合、カスタムフックは非常に強力なツールです。