Haunted — это легковесная библиотека для создания веб-компонентов на
основе стандартов Web Components с использованием реактивного подхода,
похожего на React Hooks. Одной из ключевых возможностей Haunted является
работа с эффектами (useEffect), которые
позволяют управлять побочными действиями компонентов. В отличие от
асинхронных эффектов, синхронные выполняются немедленно после
рендеринга, обеспечивая точное управление состоянием DOM и
взаимодействиями с внешними библиотеками.
В Haunted синхронные эффекты создаются с помощью
useEffect, передавая в него функцию, которая возвращает
функцию очистки (cleanup), если это необходимо.
Синхронные эффекты вызываются после того, как компонент обновил DOM, но
до завершения следующего цикла браузера. Это позволяет:
Пример базового синхронного эффекта:
import { html, component, useEffect, useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Синхронный эффект: текущий count =', count);
return () => console.log('Очистка эффекта для count =', count);
}, [count]);
return html`
<div>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
</div>
`;
}
customElements.define('my-counter', component(Counter));
Ключевой момент: функция эффекта вызывается после каждого рендера, где изменились зависимости. Если зависимости не указаны, эффект выполняется после каждого рендера компонента.
В Haunted все эффекты по умолчанию синхронные. Основное отличие заключается в том, что асинхронные действия (например, fetch-запросы, таймеры, события) должны быть обёрнуты внутри эффекта, но сам эффект при этом запускается синхронно после рендера.
Пример асинхронной работы внутри синхронного эффекта:
useEffect(() => {
let cancelled = false;
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
if (!cancelled) {
setData(data);
}
}
fetchData();
return () => {
cancelled = true; // предотвращение обновления после размонтирования
};
}, []);
Даже если внутренняя функция асинхронная, сама функция эффекта выполняется синхронно.
Каждый вызов useEffect может возвращать функцию очистки.
Это особенно важно для предотвращения утечек памяти при работе с
подписками, таймерами или сторонними библиотеками, которые модифицируют
DOM.
Пример использования функции очистки:
useEffect(() => {
const intervalId = setInterval(() => {
console.log('Таймер срабатывает каждые 1 секунду');
}, 1000);
return () => clearInterval(intervalId);
}, []);
Функция очистки вызывается перед повторным выполнением эффекта и при размонтировании компонента.
Аргумент dependencies позволяет Haunted оптимизировать
вызовы эффектов, выполняя их только при изменении указанных
значений.
[] — эффект вызовется только один раз при
монтировании.[value1, value2] — эффект вызовется при изменении
value1 или value2.Пример:
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
В этом примере синхронный эффект обеспечивает корректное подключение и удаление обработчика события.
Если компонент содержит несколько useEffect, они
вызываются в порядке объявления. Очистка выполняется
также в обратном порядке. Это позволяет контролировать порядок
инициализации ресурсов и их освобождения.
useEffect(() => {
console.log('Эффект 1');
return () => console.log('Очистка эффекта 1');
}, []);
useEffect(() => {
console.log('Эффект 2');
return () => console.log('Очистка эффекта 2');
}, []);
Вывод в консоли:
Эффект 1
Эффект 2
Очистка эффекта 2
Очистка эффекта 1
Эти задачи требуют точного момента выполнения после рендера и корректной очистки при обновлениях.
Синхронные эффекты в Haunted обеспечивают гибкое и детерминированное управление побочными действиями компонентов, позволяя создавать реактивные, отзывчивые и безопасные веб-приложения.