Сложные формы

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


Управляемые состояния формы

В Haunted состояния создаются с помощью хука useState. Для сложной формы рекомендуется хранить все поля в одном объекте состояния:

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

function ComplexForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    password: '',
    preferences: {
      newsletter: false,
      notifications: true
    }
  });

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    if (type === 'checkbox') {
      setFormData(prev => ({
        ...prev,
        preferences: {
          ...prev.preferences,
          [name]: checked
        }
      }));
    } else {
      setFormData(prev => ({ ...prev, [name]: value }));
    }
  };

  return html`
    <form>
      <input name="name" .value=${formData.name} @input=${handleChange} placeholder="Имя">
      <input name="email" .value=${formData.email} @input=${handleChange} placeholder="Email">
      <input name="password" type="password" .value=${formData.password} @input=${handleChange} placeholder="Пароль">
      <label>
        <input type="checkbox" name="newsletter" .checked=${formData.preferences.newsletter} @change=${handleChange}>
        Подписка на новости
      </label>
      <label>
        <input type="checkbox" name="notifications" .checked=${formData.preferences.notifications} @change=${handleChange}>
        Уведомления
      </label>
    </form>
  `;
}

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

Особенности подхода:

  • Все поля объединены в один объект состояния formData.
  • Для чекбоксов используется проверка type === 'checkbox'.
  • Состояние обновляется через функцию setFormData, что сохраняет реактивность интерфейса.

Валидация полей формы

Haunted не предоставляет встроенной системы валидации, но легко интегрируется с кастомными функциями. Валидация может выполняться на лету или при отправке формы:

const validateForm = (data) => {
  const errors = {};
  if (!data.name) errors.name = 'Имя обязательно';
  if (!/\S+@\S+\.\S+/.test(data.email)) errors.email = 'Неверный email';
  if (data.password.length < 6) errors.password = 'Пароль слишком короткий';
  return errors;
};

const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
  e.preventDefault();
  const validationErrors = validateForm(formData);
  setErrors(validationErrors);
  if (Object.keys(validationErrors).length === 0) {
    console.log('Форма корректна', formData);
  }
};

Рекомендации:

  • Ошибки хранятся отдельно, что позволяет легко отображать их рядом с полем.
  • Использование Object.keys(errors).length удобно для проверки полного прохождения валидации.

Динамические поля

Сложные формы часто содержат динамические элементы, например, список контактов, который пользователь может расширять:

const [contacts, setContacts] = useState([{ type: 'email', value: '' }]);

const addContact = () => {
  setContacts([...contacts, { type: 'email', value: '' }]);
};

const updateContact = (index, newValue) => {
  const updated = contacts.map((c, i) => i === index ? { ...c, value: newValue } : c);
  setContacts(updated);
};

html`
  <div>
    ${contacts.map((contact, i) => html`
      <input .value=${contact.value} @input=${e => updateContact(i, e.target.value)}>
    `)}
    <button type="button" @click=${addContact}>Добавить контакт</button>
  </div>
`;

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

  • Динамический список хранится в массиве состояния.
  • Обновление конкретного элемента производится через map, что сохраняет иммутабельность.
  • Добавление нового элемента не нарушает существующие данные.

Использование хуков эффекта

Для сложных форм полезно отслеживать изменения состояния и выполнять побочные эффекты. В Haunted используется useEffect:

import { useEffect } from 'haunted';

useEffect(() => {
  console.log('Форма изменилась:', formData);
}, [formData]);

Применения эффекта:

  • Автосохранение данных на сервер при изменении полей.
  • Динамическое включение/выключение кнопки отправки формы.
  • Обновление связанных компонентов интерфейса.

Интеграция с кастомными элементами

Haunted легко работает с другими веб-компонентами. Например, использование кастомного date-picker:

html`
  <date-picker
    .value=${formData.birthDate}
    @change=${e => setFormData({...formData, birthDate: e.target.value})}>
  </date-picker>
`;

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

  • Значение компонента привязывается через .value.
  • События обновления (@change) интегрируются с состоянием формы.
  • Сложные элементы, такие как селекторы или виджеты, полностью поддерживаются реактивностью Haunted.

Оптимизация рендеринга

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

  • Разделять форму на подкомпоненты, каждый со своим локальным состоянием.
  • Использовать useEffect с конкретными зависимостями, чтобы избегать ненужных вызовов.
  • Применять html шаблоны с повторяющимися элементами через map, сохраняя ключи для идентификации.

Обработка отправки и асинхронные операции

Формы часто требуют отправки данных на сервер. В Haunted это делается через стандартные промисы и async/await:

const submitForm = async (e) => {
  e.preventDefault();
  const validationErrors = validateForm(formData);
  setErrors(validationErrors);
  if (Object.keys(validationErrors).length === 0) {
    try {
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: JSON.stringify(formData),
        headers: { 'Content-Type': 'application/json' }
      });
      const result = await response.json();
      console.log('Ответ сервера:', result);
    } catch (err) {
      console.error('Ошибка отправки формы:', err);
    }
  }
};

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

  • Асинхронная отправка не блокирует UI.
  • Ошибки запроса можно отображать в интерфейсе отдельно от ошибок валидации.
  • Структура позволяет легко расширять функционал сложных форм.

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