tgoop.com/React_lib/688
Create:
Last Update:
Last Update:
Сегодня хочу поделиться с вами простым, но часто необходимым при работе приёмом: созданием и использованием кастомного хука 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, чтобы отменять запросы при новых вызовах или при размонтировании:BY React
Share with your friend now:
tgoop.com/React_lib/688
