Haunted — это библиотека для создания веб-компонентов с использованием реактивных хуков, вдохновлённых React, но работающих на нативных Custom Elements. Одной из ключевых особенностей является возможность автоматического связывания свойств компонента с HTML-атрибутами, что позволяет управлять состоянием через разметку и получать реактивное обновление при изменениях.
В Haunted свойство компонента — это обычное поле объекта, которое хранит данные компонента. Атрибуты — это значения, передаваемые через HTML. Связь между ними реализуется через reflection, то есть автоматическое преобразование значения свойства в атрибут и обратно.
Пример объявления свойства с отражением:
import { component, html } from 'haunted';
import { useState } from 'haunted';
function Counter({ count = 0 }) {
const [value, setValue] = useState(count);
return html`
<div>
<p>Счёт: ${value}</p>
<button @click=${() => setValue(value + 1)}>Увеличить</button>
</div>
`;
}
customElements.define('my-counter', component(Counter, { observedAttributes: ['count'] }));
В этом примере count — свойство, которое отражается на
атрибут. Если в HTML прописать
<my-counter count="5"></my-counter>, компонент
инициализируется с начальным значением 5. Любые изменения
свойства внутри компонента можно синхронизировать с атрибутом через
ручное присваивание this.setAttribute('count', value) или
через соответствующие хуки.
При отражении свойств в атрибуты важно учитывать, что HTML-атрибуты всегда строки. Haunted предоставляет возможности для преобразования типов:
true, отсутствие — false.Number().Пример для boolean-свойства:
function Toggle({ active = false }) {
const [state, setState] = useState(active);
return html`
<button
class=${state ? 'active' : ''}
@click=${() => setState(!state)}>
${state ? 'Вкл' : 'Выкл'}
</button>
`;
}
customElements.define(
'my-toggle',
component(Toggle, { observedAttributes: ['active'] })
);
Если в HTML добавить
<my-toggle active></my-toggle>, кнопка будет
активной. При изменении свойства state вручную можно
синхронизировать атрибут с помощью
this.toggleAttribute('active', state).
Haunted поддерживает реактивное обновление при изменении
атрибутов через механизм observedAttributes. Для
этого необходимо:
observedAttributes.attributeChangedCallback при
необходимости кастомного поведения.class MyElement extends HTMLElement {
static get observedAttributes() {
return ['value'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value') {
this.value = Number(newValue);
}
}
}
При использовании Haunted этот шаг упрощается, так как библиотека автоматически обновляет свойства, если они отражаются на атрибуты.
useRefДля более сложного взаимодействия можно использовать
useRef для прямого доступа к DOM и синхронизации состояния
с атрибутами:
import { component, html } from 'haunted';
import { useState, useRef, useEffect } from 'haunted';
function InputMirror({ text = '' }) {
const [value, setValue] = useState(text);
const inputRef = useRef();
useEffect(() => {
inputRef.current.value = value;
inputRef.current.setAttribute('text', value);
}, [value]);
return html`<input type="text" ref=${inputRef} @input=${e => setValue(e.target.value)} />`;
}
customElements.define('input-mirror', component(InputMirror, { observedAttributes: ['text'] }));
Здесь любое изменение состояния value автоматически
отражается на атрибуте text.
"true"/"false".useRef и хуки.Отражение свойств в атрибуты является фундаментальной возможностью Haunted, обеспечивающей удобное управление состоянием компонентов и совместимость с HTML-разметкой без дополнительного ручного кода.