Haunted — это библиотека для создания веб-компонентов с использованием реактивного подхода и шаблонов lit-html. Одной из ключевых концепций является Hooks Pattern, который позволяет управлять состоянием и побочными эффектами компонентов, делая код более декларативным и модульным.
Hooks в Haunted заимствованы из React, но адаптированы под веб-компоненты. Основная идея заключается в том, чтобы выделить состояние и логику из разметки компонента, обеспечивая повторное использование и читаемость кода.
useState — базовый Hook для управления локальным
состоянием компонента.
import { html, component, useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
<button @click=${() => setCount(count - 1)}>Уменьшить</button>
</div>
`;
}
customElements.define('counter-element', component(Counter));
Особенности useState:
useEffect позволяет выполнять побочные эффекты после
рендера компонента.
import { html, component, useState, useEffect } from 'haunted';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval);
}, []);
return html`<p>Прошло секунд: ${seconds}</p>`;
}
customElements.define('timer-element', component(Timer));
Ключевые моменты useEffect:
cleanup) для
предотвращения утечек памяти.useRef позволяет хранить ссылку на DOM-элемент или любое
изменяемое значение без повторного рендера.
import { html, component, useRef, useEffect } from 'haunted';
function FocusInput() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
return html`<input type="text" ref=${inputRef} />`;
}
customElements.define('focus-input', component(FocusInput));
Особенности useRef:
ref.current не
вызывает обновление компонента.useReducer предназначен для управления сложным
состоянием с помощью редьюсеров, аналогично Redux.
import { html, component, useReducer } from 'haunted';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function CounterReducer() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return html`
<p>Счётчик: ${state.count}</p>
<button @click=${() => dispatch({ type: 'increment' })}>+</button>
<button @click=${() => dispatch({ type: 'decrement' })}>-</button>
`;
}
customElements.define('counter-reducer', component(CounterReducer));
Особенности useReducer:
dispatch инициирует изменения через редьюсер.Haunted поддерживает контексты для передачи данных глубоко вниз по дереву компонентов. Создание кастомных Hooks позволяет инкапсулировать повторяющуюся логику.
import { html, component, useContext } from 'haunted';
import { createContext } from 'haunted/context';
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return html`<button class=${theme}>Кнопка с темой ${theme}</button>`;
}
Кастомные Hooks создаются как обычные функции, использующие встроенные Hooks:
import { useState, useEffect } from 'haunted';
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handler = () => setWidth(window.innerWidth);
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
return width;
}
Hooks в Haunted делают компоненты лёгкими, реактивными и удобными для масштабирования больших приложений, сохраняя при этом нативную совместимость с веб-стандартами.