Неконтролируемые поля

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

Определение неконтролируемого поля

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

Пример неконтролируемого поля:

import { html, component } from 'haunted';

function Form() {
  let inputRef;

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log(inputRef.value); // Значение берётся напрямую из DOM
  };

  return html`
    <form @submit=${handleSubmit}>
      <input type="text" .ref=${(el) => inputRef = el} />
      <button type="submit">Отправить</button>
    </form>
  `;
}

customElements.define('my-form', component(Form));

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

  • inputRef хранит ссылку на DOM-элемент <input>.
  • Свойство .value считывается при необходимости, а не синхронизируется с состоянием Haunted.
  • Нет необходимости использовать useState или useReducer для хранения значения поля.

Отличия от контролируемых полей

Контролируемое поле полностью управляется состоянием компонента через хуки Haunted:

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

function ControlledForm() {
  const [value, setValue] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log(value); // Значение берётся из состояния
  };

  return html`
    <form @submit=${handleSubmit}>
      <input type="text" .value=${value} @input=${(e) => setValue(e.target.value)} />
      <button type="submit">Отправить</button>
    </form>
  `;
}

customElements.define('controlled-form', component(ControlledForm));

Различия:

  • Контролируемое поле обновляет состояние при каждом событии input.
  • Неконтролируемое поле хранит значение исключительно в DOM.
  • Контролируемое поле позволяет Haunted автоматически перерисовывать компонент при изменении значения, неконтролируемое — нет.

Использование ref для неконтролируемых полей

Haunted поддерживает привязку DOM-элементов через функцию ref:

<input .ref=${el => inputRef = el} />

Эта конструкция гарантирует, что переменная inputRef содержит актуальный элемент DOM после рендера. В отличие от React, Haunted не использует виртуальный DOM, поэтому доступ к реальному элементу прямой и эффективный.

Прямое управление DOM

Неконтролируемые поля позволяют выполнять следующие операции:

  • Чтение значения через .value.
  • Изменение значения напрямую: inputRef.value = 'новое значение'.
  • Фокусировка на элементе: inputRef.focus().
  • Выбор текста: inputRef.select().

Пример автоматической установки значения при монтировании:

import { useEffect } from 'haunted';

function AutofillForm() {
  let inputRef;

  useEffect(() => {
    inputRef.value = 'Предустановленное значение';
    inputRef.focus();
  }, []);

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

customElements.define('autofill-form', component(AutofillForm));

Когда использовать неконтролируемые поля

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

Рекомендации по сочетанию с Haunted

  • Неконтролируемые поля удобно комбинировать с хуками useEffect для установки начальных значений или выполнения действий после рендера.
  • Если требуется реактивность при изменении значения, лучше использовать контролируемые поля.
  • При использовании неконтролируемых полей важно помнить о безопасности: проверять данные перед отправкой на сервер, так как они не проходят автоматическую обработку состояния.

Практическая оптимизация

  1. Минимизировать количество рефов: не создавать отдельный ref для каждого поля, если можно собрать их через FormData при отправке формы.
  2. Использовать события формы: submit и change позволяют работать с данными без отслеживания каждого ввода.
  3. Избегать избыточного рендера: неконтролируемые поля не требуют перерисовки компонента, что повышает производительность при больших формах.

Пример сложной формы с неконтролируемыми полями

function ComplexForm() {
  let nameRef, emailRef, commentRef;

  const handleSubmit = (event) => {
    event.preventDefault();
    const data = {
      name: nameRef.value,
      email: emailRef.value,
      comment: commentRef.value,
    };
    console.log(data);
  };

  return html`
    <form @submit=${handleSubmit}>
      <input type="text" placeholder="Имя" .ref=${el => nameRef = el} />
      <input type="email" placeholder="Email" .ref=${el => emailRef = el} />
      <textarea placeholder="Комментарий" .ref=${el => commentRef = el}></textarea>
      <button type="submit">Отправить</button>
    </form>
  `;
}

customElements.define('complex-form', component(ComplexForm));

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