Haunted — это современная библиотека для создания веб-компонентов с реактивным подходом, основанная на хуках, аналогичных React. Одним из ключевых аспектов при работе с веб-компонентами является управление ссылками на DOM-элементы через рефы. Форвардинг рефов позволяет передавать управление элементами от родительского компонента к дочернему, сохраняя реактивность и удобство работы с элементами DOM.
В Haunted рефы создаются с помощью хука useRef. Он
позволяет сохранить ссылку на DOM-элемент или любое другое значение,
которое должно сохраняться между рендерами компонента.
Пример создания простого рефа:
import { html, component, useRef } from 'haunted';
function MyInput() {
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus();
};
return html`
<input ${ref(inputRef)} type="text">
<button @click=${focusInput}>Фокус</button>
`;
}
customElements.define('my-input', component(MyInput));
Здесь useRef создаёт объект с ключом
current, в котором хранится ссылка на
<input>.
Если родительский компонент хочет напрямую работать с DOM-элементом
дочернего компонента, обычный useRef в дочернем компоненте
не поможет, так как реф скрыт внутри компонента. Прямой доступ к
внутреннему элементу нарушает инкапсуляцию и мешает реактивной модели
Haunted.
Форвардинг рефов решает эту проблему: родительский компонент передаёт реф в дочерний компонент, а дочерний компонент «форвардит» этот реф на нужный внутренний элемент.
Пример форвардинга:
import { html, component, useRef } from 'haunted';
import { ref } from 'lit/directives/ref.js';
function Child({ forwardedRef }) {
const internalRef = useRef();
return html`
<input ${ref(forwardedRef || internalRef)} type="text">
`;
}
function Parent() {
const parentRef = useRef();
const focusChildInput = () => {
parentRef.current.focus();
};
return html`
<child-component .forwardedRef=${parentRef}></child-component>
<button @click=${focusChildInput}>Фокус на дочернем input</button>
`;
}
customElements.define('child-component', component(Child));
customElements.define('parent-component', component(Parent));
Ключевой момент: дочерний компонент использует условное
связывание — если forwardedRef передан, он
используется, иначе создаётся внутренний реф. Это делает компонент
гибким и безопасным для использования как с форвардингом, так и без
него.
refHaunted строится поверх Lit, поэтому для связывания рефов с
DOM-элементами используется директива ref из
lit/directives/ref.js. Она обеспечивает корректное
присвоение ссылки и обновление при каждом рендере.
Форвардинг рефов позволяет не только получать доступ к DOM, но и передавать методы дочернего компонента родителю.
function Child({ forwardedRef }) {
const inputRef = useRef();
const clear = () => {
inputRef.current.value = '';
};
if (forwardedRef) {
forwardedRef.current = { focus: () => inputRef.current.focus(), clear };
}
return html`<input ${ref(inputRef)} type="text">`;
}
function Parent() {
const childRef = useRef();
const focusChild = () => childRef.current.focus();
const clearChild = () => childRef.current.clear();
return html`
<child-component .forwardedRef=${childRef}></child-component>
<button @click=${focusChild}>Фокус</button>
<button @click=${clearChild}>Очистить</button>
`;
}
Теперь родительский компонент имеет полный контроль над функционалом дочернего, сохраняя реактивность и безопасный доступ к внутренним элементам.
forwardedRef
внутри дочернего компонента, чтобы избежать ошибок при отсутствии
передачи.Форвардинг рефов в Haunted позволяет строить гибкую архитектуру компонентов, где родитель контролирует поведение дочернего элемента, а компоненты остаются реактивными и инкапсулированными. Такой подход облегчает создание сложных интерфейсов, обеспечивает повторное использование компонентов и упрощает тестирование.