[React]カスタムフックについて
カスタムフックとは
Reactのカスタムフックは、特定の機能やロジックを分離して再利用可能にすることで、コンポーネントコードの整理に非常に役立ちます。
本記事では、APIからデータを取得するためのカスタムフックを作成し、効率的なデータ取得と表示を実現する方法を紹介します。
カスタムフックの基本概念
Reactのカスタムフックは、特定の処理を他の関数やコンポーネントと分離し、コードの再利用性を高めるための関数です。useStateやuseEffectといったReactのフックを利用することで、カスタムフックは簡単にステートや副作用を管理できます。
たとえば、データ取得のロジックをカスタムフックに移動させると、異なるコンポーネントで同じ取得ロジックを使い回せるようになります。以下は、APIからデータを取得し、その結果やローディング状態、エラーメッセージを管理するカスタムフック useFetchData の例です。
実装例
以下のuseFetchDataは、任意のURLからデータを取得し、コンポーネントに返すための基本的なカスタムフックです。このフックは、データ、ローディング状態、エラーメッセージを含むオブジェクトを返します。
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の実装
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 を使ってデータを取得し、表示するコンポーネントを作成します。ボタンでタスクの状態を切り替えられるようにしてみましょう。
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アプリケーションでデータ取得をシンプルかつ効率的に実装したい場合、カスタムフックは非常に強力なツールです。
Discussion