Создание кастомного хука useFetch в React

Зачем выносить логику получения данных в хук
Современные приложения часто запрашивают данные из внешних API. Если логика запроса и обработки ответов разбросана по компонентам, появляются дублирование, сложность тестирования и риск ошибок (например, утечек при размонтировании). Кастомный хук решает эти проблемы: он инкапсулирует состояния data/loading/error и поведение (fetch, отмена, повторная попытка).
Кратко:
- Паттерн: вынести fetch в хук и вернуть { data, loading, error }.
- Польза: повторное использование, единая обработка ошибок, проще тестировать.
Простой пример: минимальный useFetch
Ниже — минимальная реализация, похожая на исходный пример. Она демонстрирует идею, но её можно улучшить.
import { useEffect, useState } from "react";
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
setError(data.error);
setData(data.joke);
setLoading(false);
});
}, [url]);
return { data, loading, error };
};
export default useFetch;Важно: в этом минимальном примере нет обработки сетевых ошибок, нет проверки response.ok и он может вызвать попытку обновления состояния после размонтирования компонента.
Усовершенствованный useFetch: обработка ошибок и отмена запросов
Ниже — более надёжная версия хука с AbortController, обработкой ошибок, повторным использованием response.json(), и корректной установкой состояния даже при ошибках.
import { useEffect, useState } from "react";
const useFetch = (url, options = {}) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
const controller = new AbortController();
const signal = controller.signal;
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const res = await fetch(url, { ...options, signal });
if (!res.ok) {
const text = await res.text();
throw new Error(`HTTP ${res.status}: ${text}`);
}
const json = await res.json();
setData(json);
} catch (err) {
if (err.name === 'AbortError') return; // отмена запроса при размонтировании
setError(err.message || 'Ошибка при загрузке данных');
} finally {
setLoading(false);
}
};
fetchData();
return () => controller.abort();
}, [url]);
return { data, loading, error };
};
export default useFetch;Пояснения:
- AbortController предотвращает попытки обновления state после размонтирования компонента.
- Проверка res.ok и чтение тела ответа помогают диагностировать ошибки сервера.
- options позволяет подставлять заголовки (например для токена авторизации).
Важно: не храните в коде секреты (API‑ключи) в открытом виде. Для приватных ключей используйте серверный прокси или защищённые переменные окружения.
Пример использования в компоненте Jokes (локализация строк)
Современный импорт выглядит так:
import useFetch from "./useFetch";Компонент с использованием хука:
const Jokes = () => {
const url = "https://sv443.net/jokeapi/v2/joke/Programming?type=single";
const { data, loading, error } = useFetch(url);
if (loading) return Загрузка...;
return (
{error && {error}}
{data && {data.joke || JSON.stringify(data)}}
);
};
export default Jokes;Совет: используйте role=”alert” для ошибок — это улучшает доступность (a11y).
Когда такой хук не подходит (контрпримеры)
- Большие приложения с множеством взаимодействующих запросов: лучше использовать React Query или SWR (кеширование, повторные попытки, фоновые обновления).
- Сложные сценарии синхронизации данных между компонентами и сервером — сторонние библиотеки дадут больше фич.
- Важно: для WebSocket или стриминга данных этот паттерн не применим напрямую.
Альтернативные подходы
- React Query (TanStack Query): готовые решения для кеширования, повторных попыток, фоновой синхронизации.
- SWR: лаконичный подход с оптимистичным обновлением и повторным валидированием.
- Axios + кастомные абстракции: если нужна расширенная конфигурация запросов.
Критерии приёмки
- Хук корректно возвращает loading=true до получения ответа.
- При успешном ответе loading=false и data содержит ответ.
- При сетевой/серверной ошибке error содержит текст ошибки.
- При размонтировании компонента запрос отменяется (AbortController).
- Тесты покрывают успешный ответ, ошибку и отмену.
Чек‑лист для разработчика
- Заголовки авторизации не содержат секретов в клиентском коде.
- Обработаны HTTP‑ошибки (res.ok).
- Присутствует логика отмены запроса.
- Ошибки показываются через role=”alert”.
- Локализация сообщений (например «Загрузка…») настроена при необходимости.
Короткая методология внедрения
- Выделите общую логику fetch в useFetch.
- Добавьте AbortController и обработку res.ok.
- Протестируйте: mock fetch, проверьте отмену и обработку 4xx/5xx.
- При необходимости замените на React Query/SWR для продвинутых сценариев.
Безопасность и приватность
- Не отправляйте персональные данные без HTTPS и без согласия пользователя.
- Для EU/GDPR: документируйте, какие данные отправляются и где они хранятся на сервере.
- Для авторизации используйте короткоживущие токены и механизм обновления на сервере.
Тестовые случаи / Acceptance
- Успешный ответ: mock fetch возвращает JSON → data заполнено, loading=false, error=null.
- Ошибка сервера: mock fetch с кодом 500 → error содержит сообщение.
- Отмена: компонент размонтируется до завершения запроса → состояние не обновляется.
Глоссарий в одну строку
- Hook: функция React, использующая хуки состояния/эффекты для общей логики.
- useEffect: хук для побочных эффектов, запускается при монтировании/обновлении.
- AbortController: API для отмены fetch‑запросов.
Резюме
Кастомный хук useFetch — простой и полезный способ централизовать логику получения данных в React. Улучшайте хук постепенно: начните с базовой реализации, добавьте обработку ошибок и отмену запросов, и затем решайте, не пора ли перейти на специализированную библиотеку (React Query или SWR) в зависимости от масштаба приложения.
Полезное: если вам нужно шаблонное поведение (кеш, фоновое обновление, объединение запросов), рассмотрите сторонние решения — они экономят время и снижают вероятность ошибок в крупном проекте.
Спасибо за внимание.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone