Реактивность свойств

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

Основы реактивности

В Haunted ключевым элементом является свойство компонента, объявляемое с помощью функции useState или useReducer. Эти свойства являются реактивными, то есть при их изменении автоматически инициируется повторный рендер компонента.

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));

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

  • useState возвращает массив [значение, функцияОбновления].
  • Любое изменение через setCount инициирует ререндер компонента.
  • Шаблон использует теговую функцию html, которая отслеживает изменения и обновляет DOM.

Свойства и атрибуты компонента

Помимо локального состояния через useState, Haunted поддерживает реактивные свойства, передаваемые через атрибуты. Для этого используется observedAttributes и привязка значений через конструктор компонента или декораторы.

import { component, html, useState } from 'haunted';

function Greeting({ name }) {
  const [greet, setGreet] = useState(`Привет, ${name}!`);
  
  return html`<p>${greet}</p>`;
}

Greeting.observedAttributes = ['name'];

customElements.define('my-greeting', component(Greeting));
  • При изменении атрибута name компонент повторно рендерится с новым значением.
  • Значения атрибутов автоматически преобразуются в свойства JavaScript через observedAttributes.

Реактивные вычисляемые свойства

Haunted позволяет создавать вычисляемые значения, которые зависят от одного или нескольких состояний. Такие значения можно реализовать через useEffect или простую функцию внутри компонента.

import { html, component, useState, useEffect } from 'haunted';

function DoubleCounter() {
  const [count, setCount] = useState(0);
  const [double, setDouble] = useState(count * 2);

  useEffect(() => {
    setDouble(count * 2);
  }, [count]);

  return html`
    <p>Счетчик: ${count}</p>
    <p>Удвоенное значение: ${double}</p>
    <button @click=${() => setCount(count + 1)}>Увеличить</button>
  `;
}

customElements.define('double-counter', component(DoubleCounter));

Особенности:

  • useEffect позволяет реагировать на изменения зависимостей [count].
  • Любое обновление состояния count автоматически пересчитает double.
  • Эффект можно использовать для синхронизации с внешними API, localStorage или другими реактивными потоками данных.

Глубокие изменения и объекты

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

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

// Правильное обновление
setUser({ ...user, age: user.age + 1 });

// Неправильное обновление (не вызовет ререндер)
user.age += 1;
  • Создание нового объекта гарантирует повторный рендер.
  • Этот подход аналогичен работе с React и Immutable-паттернами.

Связывание нескольких состояний

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

function Profile() {
  const [name, setName] = useState('Алексей');
  const [age, setAge] = useState(25);

  return html`
    <p>Имя: ${name}</p>
    <p>Возраст: ${age}</p>
    <button @click=${() => setAge(age + 1)}>Добавить год</button>
    <button @click=${() => setName('Мария')}>Изменить имя</button>
  `;
}

customElements.define('user-profile', component(Profile));
  • Каждый вызов useState управляет отдельной реактивной переменной.
  • Все изменения вызывают обновление только тех частей DOM, которые используют измененные значения.

Практические рекомендации

  • Для оптимизации ререндеров рекомендуется разделять состояния на мелкие части.
  • Использовать useEffect для побочных эффектов, не связанных напрямую с DOM.
  • Всегда создавать новые объекты или массивы при изменении данных, чтобы сохранить реактивность.
  • Атрибуты компонентов должны быть синхронизированы через observedAttributes, если предполагается их динамическое изменение.

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