Haunted — это современная библиотека для создания веб-компонентов с реактивным подходом, основанная на функциональных компонентах и хуках, аналогичных React. Одним из ключевых инструментов для управления состоянием является useState, но при работе со сложными компонентами часто возникает необходимость управлять множественными состояниями одновременно.
Haunted позволяет создавать любое количество независимых состояний с
помощью useState. Каждый вызов useState
возвращает пару: текущее значение и функцию для его обновления.
Пример:
import { component, useState, html } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
const [step, setStep] = useState(1);
return html`
<div>
<p>Счётчик: ${count}</p>
<button @click=${() => setCount(count + step)}>Увеличить</button>
<input type="number" .value=${step} @input=${e => setStep(+e.target.value)} />
</div>
`;
}
customElements.define('multi-counter', component(Counter));
В этом примере две независимые переменные состояния позволяют управлять счётчиком и величиной шага отдельно. Такой подход удобен, когда состояния не зависят друг от друга.
Для группировки связанных состояний часто используют объект
состояния. В этом случае useState хранит один
объект с несколькими полями:
function Form() {
const [formData, setFormData] = useState({ name: '', email: '' });
const handleChange = e => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
return html`
<form>
<input name="name" .value=${formData.name} @input=${handleChange} placeholder="Имя" />
<input name="email" .value=${formData.email} @input=${handleChange} placeholder="Email" />
<p>Данные: ${JSON.stringify(formData)}</p>
</form>
`;
}
customElements.define('form-component', component(Form));
Такой подход уменьшает количество вызовов useState и
позволяет легко управлять комплексными формами. Важно
всегда использовать иммутабельное обновление объекта,
чтобы Haunted корректно отслеживал изменения.
Иногда одно состояние зависит от других. В Haunted нет встроенного
хука useEffect как в React, но есть useEffect
для побочных эффектов. Пример синхронизации производного состояния:
import { component, useState, useEffect, html } from 'haunted';
function ScoreTracker() {
const [score, setScore] = useState(0);
const [bonus, setBonus] = useState(0);
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(score + bonus);
}, [score, bonus]);
return html`
<div>
<p>Очки: ${score}</p>
<p>Бонус: ${bonus}</p>
<p>Итого: ${total}</p>
<button @click=${() => setScore(score + 1)}>Добавить очко</button>
<button @click=${() => setBonus(bonus + 2)}>Добавить бонус</button>
</div>
`;
}
customElements.define('score-tracker', component(ScoreTracker));
Ключевой момент: useEffect позволяет
реагировать на изменения нескольких состояний одновременно и вычислять
производные значения без риска нарушения реактивности.
Для управления множеством однотипных данных часто используют массивы:
function TodoList() {
const [todos, setTodos] = useState([
{ text: 'Купить хлеб', done: false },
{ text: 'Сделать домашку', done: false },
]);
const toggleTodo = index => {
setTodos(todos.map((todo, i) => i === index ? { ...todo, done: !todo.done } : todo));
};
return html`
<ul>
${todos.map((todo, index) => html`
<li @click=${() => toggleTodo(index)} style="text-decoration: ${todo.done ? 'line-through' : 'none'}">
${todo.text}
</li>
`)}
</ul>
`;
}
customElements.define('todo-list', component(TodoList));
Такой подход позволяет реагировать на изменения конкретных элементов массива, сохраняя реактивность всего компонента.
function Dashboard() {
const [user, setUser] = useState({ name: '', age: 0 });
const [stats, setStats] = useState({ points: 0, level: 1 });
const [notifications, setNotifications] = useState([]);
useEffect(() => {
if (stats.points > 100) setStats({ ...stats, level: stats.level + 1 });
}, [stats.points]);
return html`
<div>
<h2>${user.name || 'Гость'}, уровень ${stats.level}</h2>
<p>Очки: ${stats.points}</p>
<button @click=${() => setStats({ ...stats, points: stats.points + 10 })}>Добавить очки</button>
<ul>
${notifications.map(n => html`<li>${n}</li>`)}
</ul>
</div>
`;
}
customElements.define('dashboard-component', component(Dashboard));
В этом компоненте несколько состояний объединены
логически: пользовательские данные, статистика и уведомления.
useEffect следит за изменением очков и автоматически
повышает уровень.
Множественные состояния в Haunted обеспечивают гибкость и модульность компонентов, позволяя создавать реактивные интерфейсы с минимальным количеством повторяющегося кода, сохраняя при этом простоту управления и предсказуемость поведения.