Ссылки на DOM-элементы

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


Создание и использование ref

В Haunted используется хук useRef для создания ссылки на DOM-элемент. С помощью этой ссылки можно напрямую взаимодействовать с элементом: изменять его свойства, вызывать методы или подписываться на события.

Пример создания рефа:

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

function MyComponent() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

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

customElements.define('my-component', component(MyComponent));

Пояснения:

  • useRef(null) создаёт объект с полем current, которое хранит ссылку на DOM-элемент.
  • Атрибут ref=${inputRef} автоматически привязывает DOM-элемент к свойству current.
  • После рендера inputRef.current содержит актуальный элемент <input>.

Отличие ref от querySelector

В веб-компонентах с Shadow DOM использование querySelector часто ограничено областью компонента. Ref в Haunted позволяет:

  • Получить прямой доступ к элементу без поиска в DOM.
  • Обеспечить стабильную ссылку между рендерами.
  • Работать с элементом внутри реактивного цикла Haunted.

Пример сравнения:

// Использование querySelector (менее безопасно в Shadow DOM)
const input = this.shadowRoot.querySelector('input');

// Использование ref (более безопасно)
const input = inputRef.current;

Ref обеспечивает безопасный доступ к элементу без необходимости учитывать Shadow DOM или уникальные селекторы.


Ссылки на несколько элементов

Если нужно управлять коллекцией элементов, можно использовать массив рефов. Это удобно для динамических списков или повторяющихся компонентов.

function ListComponent({ items }) {
  const refs = items.map(() => useRef(null));

  const highlightAll = () => {
    refs.forEach(r => {
      if (r.current) r.current.style.backgroundColor = 'yellow';
    });
  };

  return html`
    <ul>
      ${items.map((item, index) => html`
        <li ref=${refs[index]}>${item}</li>
      `)}
    </ul>
    <button @click=${highlightAll}>Выделить все</button>
  `;
}

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

  • Для каждого элемента создаётся отдельный ref.
  • После рендера каждый refs[index].current указывает на конкретный <li> элемент.
  • Это решение подходит для статических и динамических списков, если порядок элементов не меняется между рендерами.

Комбинация ref с хуком useEffect

Иногда требуется выполнить действие после монтирования компонента или после обновления DOM. В Haunted для этого используется useEffect.

import { useEffect } from 'haunted';

function FocusComponent() {
  const inputRef = useRef(null);

  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []); // пустой массив — эффект только после первого рендера

  return html`<input ref=${inputRef} />`;
}

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

  • useEffect с пустым массивом зависимостей выполняется только после первого рендера.
  • Можно безопасно обращаться к inputRef.current, так как элемент уже смонтирован.
  • Позволяет инициировать сторонние библиотеки, анимации или подписки на события после рендера.

Ссылки на кастомные компоненты и методы

Ref в Haunted можно использовать не только для стандартных HTML-элементов, но и для кастомных веб-компонентов. Это позволяет вызывать их методы или читать свойства напрямую.

function WrapperComponent() {
  const childRef = useRef(null);

  const callMethod = () => {
    if (childRef.current) {
      childRef.current.customMethod();
    }
  };

  return html`
    <my-child ref=${childRef}></my-child>
    <button @click=${callMethod}>Вызвать метод дочернего компонента</button>
  `;
}

Примечания:

  • childRef.current указывает на экземпляр веб-компонента.
  • Можно использовать любые публичные методы или свойства компонента.
  • Важно помнить, что доступ возможен только после рендера.

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

  • Всегда инициализировать ref через useRef(null).
  • Не присваивать ref напрямую в JSX/HTML без использования ref=${myRef} — это нарушает реактивность.
  • Для динамических списков использовать массивы или объекты рефов.
  • Комбинировать ref с useEffect для безопасного взаимодействия с DOM после рендера.
  • Ref не следует использовать для управления состоянием — он предназначен исключительно для прямого доступа к элементам.

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