Кастомный хук для Fetch
React-хук
useFetch— загружает данные с API, управляет состояниемloading/error/dataи отменяет запрос при размонтировании.
Задача
В React-компонентах нужно загружать данные и управлять состояниями загрузки, ошибки и данных. Дублировать useEffect + fetch в каждом компоненте неудобно — нужен переиспользуемый хук.
Решение
// hooks/useFetch.ts
import { useState, useEffect, useCallback } from 'react';
interface FetchState<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: => void;
}
function useFetch<T>(url: string, options?: RequestInit): FetchState<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const [tick, setTick] = useState(0); // триггер для refetch
const refetch = useCallback( => setTick((n) => n + 1), );
useEffect(() => {
const controller = new AbortController();
setLoading(true);
setError(null);
fetch(url, { ...options, signal: controller.signal })
.then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<T>;
})
.then((json) => {
setData(json);
setLoading(false);
})
.catch((err: Error) => {
if (err.name === 'AbortError') return; // компонент размонтирован
setError(err);
setLoading(false);
});
return => controller.abort();
}, [url, tick]); // eslint-disable-line react-hooks/exhaustive-deps
return { data, loading, error, refetch };
}
export default useFetch;
Использование:
import useFetch from '@/hooks/useFetch';
interface User {
id: number;
name: string;
email: string;
}
function UserProfile({ userId }: { userId: number }) {
const { data: user, loading, error, refetch } = useFetch<User>(`/api/users/${userId}`);
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error.message} <button onClick={refetch}>Повторить</button></p>;
if (!user) return null;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
Ключевые моменты
AbortController— отменяет запрос при размонтировании компонента; предотвращаетsetStateна размонтированный компонент.tickв зависимостяхuseEffect— позволяет вызватьrefetchбез измененияurl.err.name === 'AbortError'— AbortController бросает ошибку при отмене; её нужно игнорировать.- Хук возвращает
refetch— удобно для кнопки «Повторить» при ошибке.
Варианты
- SWR (
useSWR) — кэширование, revalidation, optimistic updates — для продакшн-приложений. - TanStack Query (
useQuery) — мощнее SWR, поддерживает мутации, пагинацию, infinite scroll. - React 19
use— нативный способ загрузки данных через Suspense.