Обновление состояния

Haunted — это библиотека для создания веб-компонентов с использованием реактивного состояния на базе стандартов Web Components и шаблонов lit-html. Основной принцип управления состоянием в Haunted строится вокруг хука useState, аналогичного React, но с особенностями, оптимизированными для нативных элементов.


Хук useState

useState позволяет создавать реактивные значения внутри компонента. Синтаксис следующий:

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>
    </div>
  `;
}

customElements.define('my-counter', component(Counter));

Ключевые моменты:

  • count — текущее значение состояния.
  • setCount — функция для обновления состояния. Любое изменение автоматически триггерит повторный рендер компонента.
  • Использование массива [значение, функция] стандартно для useState.

Асинхронные обновления состояния

Haunted обновляет компоненты синхронно в пределах текущего цикла событий. Для сложных сценариев обновления состояния полезно использовать коллбэк:

setCount(prev => prev + 1);

Здесь prev — текущее состояние на момент вызова, что предотвращает ошибки при множественных последовательных обновлениях.


Состояние объектов и массивов

Для работы с объектами и массивами важно помнить о неизменяемости данных, иначе Haunted не сможет определить изменения и не перерисует компонент.

const [user, setUser] = useState({ name: 'Иван', age: 25 });

function updateName(newName) {
  setUser({ ...user, name: newName });
}

Мутация объекта напрямую (user.name = 'Пётр') не вызовет ререндер, потому что Haunted отслеживает только замену значения, а не его внутренние изменения.


Множественные состояния

Можно использовать несколько состояний в одном компоненте:

const [count, setCount] = useState(0);
const [active, setActive] = useState(false);

Каждое состояние независимое, обновление одного не затрагивает другие, но повторный рендер охватывает весь компонент.


Производные состояния

Производные значения можно вычислять прямо в теле функции компонента:

const [count, setCount] = useState(0);
const doubled = count * 2;

doubled не хранится в useState, оно пересчитывается при каждом рендере. Для более сложных вычислений, зависящих от нескольких состояний, можно использовать комбинацию хуков и мемоизации.


Реакция на внешние события

Состояние можно обновлять на основе внешних событий, например таймеров или веб-сокетов:

const [time, setTime] = useState(new Date());

useEffect(() => {
  const interval = setInterval(() => setTime(new Date()), 1000);
  return () => clearInterval(interval);
}, []);

useEffect с пустым массивом зависимостей обеспечивает запуск кода один раз при монтировании компонента и очистку при размонтировании.


Производительность и ререндер

Haunted ререндерит компонент целиком при каждом изменении состояния. Чтобы минимизировать лишние вычисления:

  • Изменять только необходимое состояние.
  • Использовать неизменяемые структуры данных.
  • Выносить тяжелые вычисления за пределы функции рендера или применять мемоизацию.

Состояние и события пользовательского интерфейса

Обновление состояния напрямую связано с событиями. Haunted упрощает связывание событий с состоянием через синтаксис @event=${callback}:

<button @click=${() => setCount(count + 1)}>+</button>

События можно использовать не только для кликов, но и для ввода текста, перемещения мыши, изменения элементов формы. Любое изменение через функцию обновления состояния автоматически перерисует компонент с новыми данными.


Сложные структуры состояния

Для комплексных компонентов часто используют вложенные объекты или массивы с динамическими элементами:

const [tasks, setTasks] = useState([
  { id: 1, title: 'Учить Haunted', done: false },
  { id: 2, title: 'Сделать проект', done: false }
]);

function toggleTask(id) {
  setTasks(tasks.map(task =>
    task.id === id ? { ...task, done: !task.done } : task
  ));
}

Такой подход обеспечивает реактивность и корректное обновление DOM при изменении любого элемента коллекции.


Итоговые рекомендации

  • Всегда использовать setState для изменения состояния.
  • Избегать прямых мутаций объектов и массивов.
  • Для сложных или зависимых состояний использовать комбинацию useState и useEffect.
  • Разделять состояние на логические части для минимизации повторных рендеров.

Haunted предоставляет мощный и гибкий механизм управления состоянием, который, при соблюдении принципов неизменяемости и правильного связывания событий, позволяет создавать производительные и легко расширяемые веб-компоненты.