Валидация форм

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

Контролируемые элементы

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

import { h } from 'preact';
import { useState } from 'preact/hooks';

function LoginForm() {
  const [email, setEmail] = useState('');
  
  const handleChange = (e) => {
    setEmail(e.target.value);
  };

  return (
    <form>
      <input
        type="email"
        value={email}
        onIn put={handleChange}
        placeholder="Введите email"
      />
    </form>
  );
}

Ключевой момент: значение поля привязано к состоянию компонента. Это позволяет легко выполнять проверку данных при вводе или при отправке формы.

Валидация при вводе

Для валидации на лету можно использовать дополнительное состояние для ошибок:

const [error, setError] = useState('');

const validateEmail = (value) => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(value) ? '' : 'Некорректный email';
};

const handleChange = (e) => {
  const value = e.target.value;
  setEmail(value);
  setError(validateEmail(value));
};

Вывод ошибки:

<p style={{ color: 'red' }}>{error}</p>

Таким образом, ошибки отображаются динамически, а состояние компонента всегда актуально.

Валидация при отправке формы

Иногда проверка всех полей выполняется при попытке отправки формы:

const handleSubmit = (e) => {
  e.preventDefault();
  const emailError = validateEmail(email);
  if (emailError) {
    setError(emailError);
    return;
  }
  console.log('Форма отправлена', email);
};

Обработка событий onSubmit позволяет предотвратить отправку некорректных данных.

Многоуровневая валидация

Для больших форм с несколькими полями используется объект состояния для хранения значений и ошибок:

const [formData, setFormData] = useState({ email: '', password: '' });
const [formErrors, setFormErrors] = useState({});

const handleChange = (field) => (e) => {
  const value = e.target.value;
  setFormData({ ...formData, [field]: value });
  setFormErrors({ ...formErrors, [field]: '' });
};

const validateForm = () => {
  const errors = {};
  if (!formData.email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
    errors.email = 'Некорректный email';
  }
  if (formData.password.length < 6) {
    errors.password = 'Пароль должен быть не менее 6 символов';
  }
  return errors;
};

const handleSubmit = (e) => {
  e.preventDefault();
  const errors = validateForm();
  if (Object.keys(errors).length) {
    setFormErrors(errors);
    return;
  }
  console.log('Форма отправлена', formData);
};

Отображение ошибок для каждого поля:

<input
  type="password"
  value={formData.password}
  onIn put={handleChange('password')}
  placeholder="Введите пароль"
/>
<p style={{ color: 'red' }}>{formErrors.password}</p>

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

Для повторного использования логики валидации удобно создавать кастомные хуки:

import { useState } from 'preact/hooks';

function useForm(initialValues, validate) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});

  const handleChange = (field) => (e) => {
    const value = e.target.value;
    setValues({ ...values, [field]: value });
    if (errors[field]) {
      setErrors({ ...errors, [field]: '' });
    }
  };

  const handleSubmit = (onSuccess) => (e) => {
    e.preventDefault();
    const validationErrors = validate(values);
    if (Object.keys(validationErrors).length) {
      setErrors(validationErrors);
      return;
    }
    onSuccess(values);
  };

  return { values, errors, handleChange, handleSubmit };
}

Использование хука:

const { values, errors, handleChange, handleSubmit } = useForm(
  { email: '', password: '' },
  (vals) => {
    const errs = {};
    if (!vals.email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) errs.email = 'Некорректный email';
    if (vals.password.length < 6) errs.password = 'Пароль слишком короткий';
    return errs;
  }
);

Динамическая и условная валидация

В сложных сценариях некоторые поля могут требовать проверки только при определённых условиях:

if (formData.subscribe && !formData.phone) {
  errors.phone = 'Телефон обязателен при подписке';
}

Это позволяет гибко настраивать правила валидации в зависимости от состояния формы.

Итоговая структура формы

Современные Preact-формы строятся вокруг:

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

Такой подход упрощает поддержку больших форм, повышает читаемость кода и обеспечивает гибкость при реализации сложных правил валидации.