Сегодня хочу поделиться с вами простым, но часто необходимым при работе приёмом: создан... — 27 мая 2026 г. в 12:22:06.962
Сегодня хочу поделиться с вами простым, но часто необходимым при работе приёмом: созданием и использованием кастомного хука useFetch для загрузки данных. Часто в React-компонентах мы дублируем один и тот же код: ставим загрузку, устанавливаем состояние для data, error и loading, пишем useEffect, чтобы делать вызов API, очищаем эффекты… Всё это можно обобщить в одном месте и переиспользовать во множестве компонент. Вот базовая реализация хука useFetch: import { useState, useEffect, useRef } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Чтобы избежать обновления стейта после размонтирования компонента const isMounted = useRef(true); useEffect(() => { // При монтировании флага меняются isMounted.current = true; // Начинаем загрузку fetch(url) .then(response => { if (!response.ok) { throw new Error(`Ошибка ${response.status}`); } return response.json(); }) .then(json => { if (isMounted.current) { setData(json); setLoading(false); } }) .catch(err => { if (isMounted.current) { setError(err.message); setLoading(false); } }); // Очистка: помечаем, что компонент размонтирован return () => { isMounted.current = false; }; }, [url]); return { data, loading, error }; } Разбор ключевых моментов: 1️⃣ useRef для флага isMounted Если компонент размонтируется до того, как придёт ответ от сервера, вызов setState внутри промиса может вызвать утечку памяти и предупреждение React. Флаг isMounted.current помогает проверить, что компонент ещё жив. 2️⃣ Состояния data, loading, error Вынесли все три состояния в хук — теперь в компоненте не нужно повторять одно и то же. Достаточно будет написать: const { data, loading, error } = useFetch('https://api.example.com/posts'); 3️⃣ Параметр url в массиве зависимостей Если url меняется, хук автоматически запустит новую загрузку. 4️⃣ Обработка ошибок Мы сразу проверяем response.ok, иначе бросаем исключение, и в .catch устанавливаем error. Теперь пример использования хука в компоненте: import React from 'react'; import useFetch from './hooks/useFetch'; function PostsList() { const { data: posts, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts'); if (loading) { return <div>Загрузка...</div>; } if (error) { return <div>Ошибка: {error}</div>; } return ( <div> <h2>Список постов</h2> <ul> {posts.map(post => ( <li key={post.id}> <strong>{post.title}</strong> <p>{post.body}</p> </li> ))} </ul> </div> ); } export default PostsList; Плюсы такого подхода: * Меньше дублирования кода. Вместо того чтобы копипастить один и тот же useEffect в десятке компонентов, просто импортируем useFetch. * Централизованная логика. Если понадобится добавить, скажем, кеширование или отмену запроса через AbortController, меняем только внутри useFetch. * Чистый код в компонентах. Компонент сосредоточен на отображении, а все детали работы с сетью спрятаны в хук. Советы по улучшению: * Можно расширить хук, чтобы принимать не только url, но и опции fetch (метод, заголовки и т.п.). * Добавить параметр deps (зависимости), чтобы перезапускать запрос не только при изменении URL, а при любой другой переменной. * Использовать AbortController, чтобы отменять запросы при новых вызовах или при размонтировании: ✍️ @React_lib

