События форм и валидация

В Preact обработка событий форм строится на тех же принципах, что и в React, с минимальными отличиями, обусловленными легковесностью фреймворка. Основной подход заключается в использовании событий DOM, которые привязываются к JSX-элементам через свойства, начинающиеся с on.

Основные события

Для работы с формами чаще всего используются следующие события:

  • onInput — вызывается при изменении значения поля ввода, текстового поля <textarea> или селектора <select>. Предпочтительнее использовать для мгновенного отслеживания изменений.
  • onChange — срабатывает при подтверждении изменения значения (например, после потери фокуса в текстовом поле или выбора опции). В Preact отличается от стандартного DOM change, так как выполняется через синтетическую систему событий.
  • onSubmit — привязывается к тегу <form> и срабатывает при отправке формы. Позволяет перехватывать событие и предотвращать стандартное поведение браузера с помощью event.preventDefault().

Пример базового обработчика:

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

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

    const handleInput = (e) => {
        setValue(e.target.value);
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Отправлено:', value);
    };

    return (
        <form onSub mit={handleSubmit}>
            <input type="text" value={value} onIn put={handleInput} />
            <button type="submit">Отправить</button>
        </form>
    );
}

Отличия Preact от React

  1. Preact не использует полноценный синтетический слой событий React, что делает обработку событий более легкой и быстрой, но накладывает ограничения на использование некоторых нестандартных событий.
  2. Атрибуты событий пишутся в camelCase (onInput, onChange) и привязываются к JSX-элементам напрямую, без необходимости оборачивать в addEventListener.
  3. Управляемые компоненты (controlled components) работают так же, как в React: значение элемента привязано к состоянию, а изменения состояния происходят через обработчики событий.

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

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

Простая валидация

Часто применяется для обязательных полей и проверки минимальной длины:

function FormWithValidation() {
    const [email, setEmail] = useState('');
    const [error, setError] = useState('');

    const validateEmail = (value) => {
        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return regex.test(value);
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        if (!validateEmail(email)) {
            setError('Некорректный email');
        } else {
            setError('');
            console.log('Форма успешно отправлена:', email);
        }
    };

    return (
        <form onSub mit={handleSubmit}>
            <input
                type="email"
                value={email}
                onIn put={(e) => setEmail(e.target.value)}
            />
            {error && <span style={{ color: 'red' }}>{error}</span>}
            <button type="submit">Отправить</button>
        </form>
    );
}

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

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

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

    const handleChange = (e) => {
        const { name, value } = e.target;
        setValues({ ...values, [name]: value });
        if (validate) {
            const validationErrors = validate({ ...values, [name]: value });
            setErrors(validationErrors);
        }
    };

    return { values, errors, handleChange };
}

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

function RegistrationForm() {
    const validate = (values) => {
        const errors = {};
        if (!values.username) errors.username = 'Имя обязательно';
        if (!values.password || values.password.length < 6) errors.password = 'Пароль слишком короткий';
        return errors;
    };

    const { values, errors, handleChange } = useForm({ username: '', password: '' }, validate);

    const handleSubmit = (e) => {
        e.preventDefault();
        if (!Object.keys(errors).length) {
            console.log('Регистрация прошла успешно', values);
        }
    };

    return (
        <form onSub mit={handleSubmit}>
            <input
                type="text"
                name="username"
                value={values.username}
                onIn put={handleChange}
            />
            {errors.username && <span>{errors.username}</span>}

            <input
                type="password"
                name="password"
                value={values.password}
                onIn put={handleChange}
            />
            {errors.password && <span>{errors.password}</span>}

            <button type="submit">Зарегистрироваться</button>
        </form>
    );
}

Ключевые принципы эффективной валидации

  • Разделение логики и отображения: обработка ошибок и значения формы управляется отдельно от JSX.
  • Мгновенная обратная связь: использование onInput позволяет отображать ошибки сразу после изменения значения.
  • Масштабируемость: кастомные хуки позволяют легко добавлять новые поля и правила валидации.
  • Избежание ненужных ререндеров: хранение ошибок и значений в отдельных состояниях минимизирует перерисовку компонентов.

Работа с нестандартными элементами форм

Preact корректно поддерживает кастомные компоненты как элементы формы, если они реализуют интерфейс контролируемого компонента:

  • value и onInput должны быть прокинуты вниз до реального DOM-элемента.
  • Событие onChange или onInput должно передавать объект события с target.value.

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

function CustomInput({ value, onInput }) {
    return (
        <input
            type="text"
            value={value}
            onIn put={onInput}
            style={{ border: '1px solid blue', padding: '4px' }}
        />
    );
}

Такой компонент можно использовать в управляемой форме так же, как обычный <input>.

Асинхронная валидация

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

const checkUsernameAvailability = async (username) => {
    const response = await fetch(`/api/check-username?username=${username}`);
    const result = await response.json();
    return result.available;
};

async function handleSubmit(e) {
    e.preventDefault();
    const available = await checkUsernameAvailability(values.username);
    if (!available) {
        setErrors({ ...errors, username: 'Имя уже занято' });
    } else {
        console.log('Имя доступно, форма готова к отправке');
    }
}

Асинхронная валидация интегрируется в хуки или непосредственно в обработчики событий, обеспечивая проверку перед окончательной отправкой формы.

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