Библиотека Haunted предоставляет функциональный
подход к созданию веб-компонентов с использованием реактивных хуков,
аналогичных React. Одним из ключевых аспектов является управление
побочными эффектами и асинхронными операциями, такими как загрузка
данных с сервера. Для этого используется хук
useEffect, который позволяет выполнять
действия после рендеринга компонента и контролировать их повторное
выполнение.
useEffectuseEffect принимает два аргумента:
useEffect(() => {
// эффект
return () => {
// очистка эффекта
};
}, [зависимости]);
componentWillUnmount).[]), эффект выполнится один раз после первого
рендера.Прямое объявление асинхронной функции внутри useEffect
не поддерживается:
useEffect(async () => {
const data = await fetchData(); // так делать нельзя
}, []);
Правильный способ — определить асинхронную функцию внутри эффекта и вызвать её:
import { html, useState, useEffect } from 'haunted';
function DataLoader() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/items');
if (!response.ok) throw new Error('Ошибка сети');
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return html`
${loading ? html`<p>Загрузка...</p>` :
error ? html`<p>Ошибка: ${error}</p>` :
html`<ul>${data.map(item => html`<li>${item.name}</li>`)}</ul>`}
`;
}
Ключевые моменты:
useEffect позволяет
использовать await.loading помогает отслеживать процесс
загрузки.try/catch гарантирует
стабильность компонента.Массив зависимостей контролирует повторный вызов эффекта. Если загрузка данных зависит от каких-либо пропсов или состояния, эти значения необходимо указать в массиве:
function UserLoader({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
const fetchUser = async () => {
const response = await fetch(`https://api.example.com/users/${userId}`);
const data = await response.json();
if (!cancelled) setUser(data);
};
fetchUser();
return () => { cancelled = true; };
}, [userId]);
return html`${user ? html`<p>${user.name}</p>` : html`<p>Загрузка...</p>`}`;
}
userId.cancelled предотвращает обновление
состояния после размонтирования компонента.При работе с асинхронными запросами важно избегать обновления состояния на размонтированном компоненте. Для этого внутри эффекта возвращается функция очистки:
useEffect(() => {
let isMounted = true;
fetch('https://api.example.com/items')
.then(res => res.json())
.then(data => {
if (isMounted) setData(data);
});
return () => { isMounted = false; };
}, []);
Это особенно важно для компонентов, которые могут часто монтироваться и размонтироваться, например в списках или при навигации.
Можно использовать несколько useEffect для разделения
логики:
useEffect(() => {
// загрузка данных
}, []);
useEffect(() => {
// подписка на события
return () => { /* отписка */ };
}, []);
Разделение эффектов повышает читаемость и управляемость кода. Каждый эффект отвечает за отдельную задачу: один за асинхронную загрузку, другой — за события или таймеры.
useEffect с кастомными хукамиДля повторного использования логики загрузки данных удобно создавать кастомные хуки:
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
const res = await fetch(url);
const json = await res.json();
if (!cancelled) setData(json);
} catch (err) {
if (!cancelled) setError(err);
} finally {
if (!cancelled) setLoading(false);
}
};
fetchData();
return () => { cancelled = true; };
}, [url]);
return { data, loading, error };
}
Использование:
function ItemsList() {
const { data, loading, error } = useFetch('https://api.example.com/items');
return html`
${loading ? html`<p>Загрузка...</p>` :
error ? html`<p>Ошибка: ${error.message}</p>` :
html`<ul>${data.map(item => html`<li>${item.name}</li>`)}</ul>`}
`;
}
useEffect.loading, error и
data формируют устойчивый паттерн для всех асинхронных
операций в Haunted.Этот подход обеспечивает безопасную, предсказуемую и реактивную работу веб-компонентов с загрузкой данных, сохраняя все преимущества функционального стиля и хуков.