Haunted — это минималистичная библиотека для создания веб-компонентов на основе Web Components и Hooks, вдохновлённая React. Основная идея заключается в том, чтобы объединить легковесность Web Components с удобством реактивного управления состоянием через хуки.
Библиотека предоставляет возможность создавать функциональные
компоненты, управлять состоянием с помощью
useState, выполнять побочные эффекты через
useEffect и работать с жизненным циклом компонента, не
отходя от стандартов браузера.
Haunted можно подключить через npm:
npm install haunted
Или использовать CDN для быстрого прототипирования:
<script type="module">
import { html, component, useState, useEffect } from 'https://unpkg.com/haunted/haunted.js?module';
</script>
Ключевые экспорты:
html — функция-шаблонизатор, аналог JSX, но на чистых
шаблонных литералах.component — функция для регистрации
веб-компонента.useState, useEffect,
useReducer, useRef, useContext —
хуки для управления состоянием и жизненным циклом.Простейший функциональный компонент выглядит так:
import { html, component } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
</div>
`;
}
customElements.define('my-counter', component(Counter));
Особенности:
html.useState создаёт реактивное состояние, при
изменении которого компонент перерисовывается.@событие=${функция}.useState(initialValue) возвращает массив
[state, setState], аналогичный React. Изменение
state вызывает повторный рендер компонента.
useEffect(callback, dependencies) позволяет выполнять
побочные эффекты. Если dependencies опущены, эффект
выполняется после каждого рендера. Если передан массив зависимостей,
эффект выполняется только при их изменении.
useEffect(() => {
console.log('Компонент обновлён');
}, [count]);
Альтернатива useState для сложной логики обновления
состояния:
const [state, dispatch] = useReducer((state, action) => {
switch(action.type) {
case 'increment': return state + 1;
default: return state;
}
}, 0);
Позволяет хранить ссылку на DOM-элемент или любое значение между рендерами:
const inputRef = useRef();
html`<input type="text" ${ref(inputRef)} />`
Haunted прекрасно подходит для управления формами благодаря реактивному состоянию. Пример формы с полями и валидацией:
function LoginForm() {
const [form, setForm] = useState({ username: '', password: '' });
const [error, setError] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!form.username || !form.password) {
setError('Все поля обязательны');
return;
}
console.log('Форма отправлена', form);
};
return html`
<form @submit=${handleSubmit}>
<label>
Логин:
<input type="text" .value=${form.username} @input=${e => setForm({ ...form, username: e.target.value })} />
</label>
<label>
Пароль:
<input type="password" .value=${form.password} @input=${e => setForm({ ...form, password: e.target.value })} />
</label>
${error && html`<p style="color:red;">${error}</p>`}
<button type="submit">Войти</button>
</form>
`;
}
customElements.define('login-form', component(LoginForm));
Ключевые моменты при работе с формами:
.value вместо value для
привязки состояния.@input позволяют синхронизировать состояние
с полями.useState,
что упрощает валидацию и обработку данных.Haunted поддерживает Context для передачи данных между
компонентами без проброса через пропсы:
import { createContext, useContext } from 'haunted';
const UserContext = createContext();
function Profile() {
const user = useContext(UserContext);
return html`<p>Пользователь: ${user.name}</p>`;
}
function App() {
return html`
<${UserContext.Provider} value=${{ name: 'Иван' }}>
<profile></profile>
<//>
`;
}
Это особенно удобно для больших приложений с множеством форм и компонентами, которым требуется доступ к общему состоянию.
Haunted легко интегрируется с библиотеками для форм, такими как
Formik или Yup, благодаря
независимости от фреймворка. Основная идея — хранить состояние формы в
useState или useReducer, а валидацию выполнять
через сторонние библиотеки внутри useEffect или
обработчиков событий.
Формы с динамическими полями и списками:
function TodoForm() {
const [todos, setTodos] = useState([{ text: '' }]);
const addTodo = () => setTodos([...todos, { text: '' }]);
const updateTodo = (index, value) => {
const newTodos = [...todos];
newTodos[index].text = value;
setTodos(newTodos);
};
return html`
<div>
${todos.map((todo, i) => html`
<input type="text" .value=${todo.text} @input=${e => updateTodo(i, e.target.value)} />
`)}
<button @click=${addTodo}>Добавить</button>
</div>
`;
}
customElements.define('todo-form', component(TodoForm));
Особенности:
useState.Haunted сочетает в себе легковесность Web Components, удобство хуков и возможности управления сложными формами, делая разработку современных реактивных интерфейсов максимально гибкой и производительной.