Haunted — это легковесная библиотека для создания веб-компонентов с использованием функционального подхода в стиле React Hooks. Несмотря на простоту, разработчики часто сталкиваются с повторяющимися ошибками, которые могут приводить к неправильной работе компонентов, утечкам памяти или сложностям с состоянием. Рассмотрим наиболее типичные из них и методы их устранения.
useState и асинхронного кодаПроблема: Часто возникает ошибка, когда состояние обновляется асинхронно, но компонент уже размонтирован. Например:
import { component, useState } from 'haunted';
function Timer() {
const [seconds, setSeconds] = useState(0);
setTimeout(() => {
setSeconds(seconds + 1);
}, 1000);
return html`<p>${seconds}</p>`;
}
customElements.define('my-timer', component(Timer));
Почему это ошибка:
setTimeout.Решение: Использовать useEffect для
управления побочными эффектами и очистки ресурсов:
import { component, useState, useEffect } from 'haunted';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(id); // очистка при размонтировании
}, []);
return html`<p>${seconds}</p>`;
}
customElements.define('my-timer', component(Timer));
useEffect с зависимостямиПроблема: Если зависимость эффекта указана неверно или отсутствует, это приводит к бесконечным ререндерингам или неинициализированным состояниям:
useEffect(() => {
fetchData().then(setData);
}, []); // пустой массив зависимостей
Почему это ошибка:
fetchData зависит от пропсов или состояния, новые данные не
будут загружаться.Решение: Тщательно определять зависимости:
useEffect(() => {
fetchData(userId).then(setData);
}, [userId]); // перезапуск при изменении userId
useRefПроблема: Попытка напрямую изменять DOM до первого рендера или через обычную переменную:
let inputEl;
function MyComponent() {
return html`<input ${ref(el => inputEl = el)} />`;
}
Почему это ошибка:
undefined.Решение: Использовать useRef из
Haunted:
import { useRef } from 'haunted';
function MyComponent() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
return html`<input ${ref(inputRef)} />`;
}
Проблема: Неявное сравнение объектов или массивов приводит к бесконечным ререндерингам:
function List({ items }) {
useEffect(() => {
console.log('Items changed');
}, [items]); // items может быть новым объектом при каждом рендере
}
Почему это ошибка:
items.
Эффект срабатывает даже если содержимое не изменилось.Решение:
useMemo или
константные ссылки:const itemsMemo = useMemo(() => items, [items.length]);
<List items={itemsMemo} />
html шаблонамиПроблема: Попытка вставить JavaScript-объект или массив напрямую в шаблон:
return html`<p>${[1,2,3]}</p>`;
Почему это ошибка:
Решение: Использовать map для создания
элементов:
return html`<ul>
${[1,2,3].map(n => html`<li>${n}</li>`)}
</ul>`;
Проблема: Подписки на события или WebSocket остаются активными после размонтирования:
useEffect(() => {
socket.on('message', handleMessage);
}, []); // отсутствие очистки
Почему это ошибка:
Решение: Очистка подписок в return
эффекта:
useEffect(() => {
socket.on('message', handleMessage);
return () => socket.off('message', handleMessage);
}, []);
Проблема: Инициализация состояния на каждом рендере приводит к сбросу значений:
const [count, setCount] = useState(Math.random());
Почему это ошибка:
Math.random(), что
сбрасывает состояние.Решение:
const [count, setCount] = useState(() => Math.random());
Эти ошибки являются самыми распространёнными при работе с Haunted. Правильное использование хуков, внимательное управление побочными эффектами и мемоизация пропсов позволяет создавать стабильные и эффективные веб-компоненты.