Утилитарные типы

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


html и шаблонные литералы

В Haunted рендеринг компонента строится через функцию html, которая возвращает шаблон с использованием template literals. Она является фундаментальным утилитарным инструментом, обеспечивающим:

  • Безопасность: автоматическое экранирование потенциально опасного HTML-кода.
  • Реактивность: пересоздание DOM при изменении зависимостей.
  • Легковесность: минимизация накладных расходов при обновлении компонентов.

Пример использования:

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

Ключевой момент: ${...} позволяет внедрять динамические значения, сохраняя реактивность без прямого вмешательства в DOM.


component как утилитарный тип

component в Haunted превращает обычную функцию в полноценный Web Component, автоматически управляя жизненным циклом. Это утилитарный тип, поскольку скрывает сложную логику регистрации и связывания с Shadow DOM.

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

  • Позволяет использовать Hooks (useState, useEffect, useRef) внутри функциональных компонентов.
  • Обеспечивает повторное использование компонентов без дублирования кода.
  • Поддерживает декларативный подход к связыванию данных и событий.

Пример:

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

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => setSeconds(s => s + 1), 1000);
    return () => clearInterval(interval);
  }, []);

  return html`<p>Прошло секунд: ${seconds}</p>`;
}

customElements.define('my-timer', component(Timer));

useState и реактивные значения

useState является утилитарным типом для управления локальным состоянием компонента. Он возвращает массив [state, setState]:

  • state — текущее значение.
  • setState — функция для обновления значения, автоматически вызывающая ререндер компонента.

Важно помнить:

  • При вызове setState с тем же значением компонент не перерендеривается.
  • Можно использовать объекты и массивы, но требуется создание нового объекта/массива для корректного обновления DOM.

Пример с объектом:

const [user, setUser] = useState({ name: 'Alice', age: 25 });

setUser({ ...user, age: user.age + 1 });

useEffect для побочных эффектов

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

Сигнатура: useEffect(callback, dependencies)

  • callback — функция, которая выполняется после рендера.
  • dependencies — массив зависимостей, при изменении которых эффект перезапускается. Пустой массив означает один вызов при монтировании.

Пример:

useEffect(() => {
  console.log('Компонент смонтирован');

  return () => console.log('Компонент размонтирован');
}, []);

useRef для управления DOM

useRef предоставляет ссылку на DOM-элемент или любое изменяемое значение, сохраняя его между рендерами:

const inputRef = useRef();

function focusInput() {
  inputRef.current.focus();
}

return html`
  <input type="text" ${ref(inputRef)} />
  <button @click=${focusInput}>Фокус на input</button>
`;

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

  • Не вызывает ререндер при изменении значения.
  • Используется для интеграции с библиотеками, требующими прямого доступа к DOM.

useReducer для сложного состояния

useReducer — утилитарный тип для управления сложными состояниями, заменяющий цепочку вызовов useState:

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

const [state, dispatch] = useReducer(reducer, { count: 0 });

return html`
  <p>Счёт: ${state.count}</p>
  <button @click=${() => dispatch({ type: 'increment' })}>+</button>
  <button @click=${() => dispatch({ type: 'decrement' })}>-</button>
`;

Преимущество useReducerясная структура управления состоянием и предсказуемость поведения при сложных изменениях.


ref и управление атрибутами

Haunted предоставляет утилиту ref для привязки DOM-элементов к переменным:

  • Используется совместно с useRef.
  • Позволяет работать с нестандартными атрибутами или сторонними библиотеками.
  • Упрощает динамическое управление состоянием без ререндеров.
import { ref, useRef } from 'haunted';

const myDivRef = useRef();

return html`
  <div ${ref(myDivRef)}>Контент</div>
`;

Комбинация утилитарных типов

Одним из сильных преимуществ Haunted является сочетание утилитарных типов. Например, использование useState с html и component позволяет создавать реактивные компоненты с минимальным количеством кода:

function Toggle() {
  const [on, setOn] = useState(false);

  return html`
    <button @click=${() => setOn(!on)}>
      ${on ? 'Включено' : 'Выключено'}
    </button>
  `;
}

customElements.define('my-toggle', component(Toggle));

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


Основные выводы по утилитарным типам Haunted

  • html — шаблонная функция для безопасного и реактивного рендеринга.
  • component — обёртка для создания Web Components с поддержкой Hooks.
  • useState, useReducer — управление локальным и сложным состоянием.
  • useEffect — управление побочными эффектами.
  • useRef и ref — работа с DOM-элементами без ререндеров.

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