В современных веб-приложениях часто возникает необходимость управлять побочными эффектами компонентов, особенно когда они включают асинхронные операции, такие как запросы к серверу, таймеры или подписки. Preact предоставляет механизмы для корректного очищения ресурсов при размонтировании компонентов, что позволяет предотвращать утечки памяти и ошибки обновления состояния у уже несуществующих элементов.
useEffect для управления побочными эффектамиВ Preact хук useEffect выполняет ту же роль, что и в
React: позволяет выполнять побочные эффекты после рендера компонента.
Важной особенностью является возможность возвращать функцию
очистки (cleanup function), которая вызывается при
размонтировании компонента или перед следующим вызовом эффекта.
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
function Example() {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch('https://api.example.com/data', { signal: controller.signal })
.then(response => response.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
// Функция очистки вызывается при размонтировании компонента
return () => controller.abort();
}, []);
return (
<div>{data ? JSON.stringify(data) : 'Загрузка...'}</div>
);
}
Ключевые моменты:
AbortController используется для отмены
fetch-запросов.useEffect, автоматически
вызывается при размонтировании компонента.err.name !== 'AbortError' предотвращает
обработку ошибки отмененного запроса как обычной ошибки.Асинхронные операции внутри компонентов могут быть не только fetch-запросами. Это таймеры, WebSocket-подключения, анимации или подписки на события. Для предотвращения утечек памяти и некорректного обновления состояния следует всегда использовать cleanup.
Пример с таймером:
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(interval);
}, []);
return <div>Секунд прошло: {count}</div>;
}
Особенности:
setInterval создаёт повторяющийся таймер, который
необходимо очищать при размонтировании.clearInterval предотвращает продолжение работы таймера
после удаления компонента.При работе с библиотеками, которые создают подписки на события или WebSocket-соединения, необходимо также использовать cleanup:
import { h } from 'preact';
import { useEffect, useState } from 'preact/hooks';
function WebSocketComponent() {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket('wss://example.com/socket');
socket.addEventListener('message', event => {
setMessages(prev => [...prev, event.data]);
});
return () => {
socket.close();
};
}, []);
return (
<ul>
{messages.map((msg, index) => <li key={index}>{msg}</li>)}
</ul>
);
}
Важные моменты:
socket.close().Даже при отмене запросов или таймеров важно убедиться, что состояние
компонента обновляется только когда компонент всё ещё монтирован.
Использование флагов isMounted часто помогает:
useEffect(() => {
let isMounted = true;
fetch('https://api.example.com/data')
.then(response => response.json())
.then(result => {
if (isMounted) setData(result);
});
return () => { isMounted = false; };
}, []);
Этот подход предотвращает “setState on unmounted component” ошибки, которые часто возникают при асинхронных операциях.
useEffect для
асинхронных операций.AbortController для fetch-запросов.Эффективное управление побочными эффектами в Preact позволяет создавать устойчивые и производительные приложения, минимизируя утечки памяти и непредсказуемое поведение при быстром монтировании и размонтировании компонентов.