tg-me.com/React_lib/688
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
Warning: Undefined variable $i in /var/www/tg-me/post.php on line 283
Share with your friend now:
tg-me.com/React_lib/688