Uncontrolled-First React Forms: React Hook Form + Zod

#react#forms#performance

Если вы работаете с большими формами в React, особенно в enterprise-приложениях, то знаете, как быстро управляемые компоненты могут стать узким местом производительности. Каждый keystroke в управляемом поле вызывает вызов setState, что приводит к повторным рендерам и заметной задержке между вводом и отображением текста. Это особенно ощутимо в формах с 20+ полями, вложенными секциями и динамическими элементами.

React Hook Form: Uncontrolled-First подход

React Hook Form (RHF) предлагает альтернативу — подход uncontrolled-first. Вместо того чтобы хранить значения в состоянии React, RHF использует refs и DOM, обновляя состояние только когда это действительно необходимо. Это позволяет существенно сократить количество рендеров и уменьшить задержку ввода. В одном из моих бенчмарков переход на RHF + Zod снизил задержку с ~80ms до <10ms, а количество рендеров с 6 до 1 на каждый keystroke.

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

  1. Используйте register для нативных input-полей
    Это позволяет работать с компонентами в uncontrolled-режиме. Controller и useController следует использовать только для сторонних управляемых компонентов.

  2. Стратегическая валидация
    Используйте zodResolver для единого источника истины, но валидируйте данные только при отправке формы (mode: 'onSubmit') или при потере фокуса (mode: 'onTouched'), а не на каждый keystroke.

  3. Избегайте глобального watch
    Вызов watch() на уровне всей формы подписывает все поля на изменения, что приводит к множественным рендерам. Вместо этого используйте useFormState для флагов уровня формы (например, isSubmitting) внутри дочернего компонента и useWatch для конкретных полей.

  4. Минимизируйте вызовы setValue и useController
    Пакетируйте программные обновления и избегайте записи значений на каждое событие ввода.

  5. Профилируйте рендеры
    Используйте React DevTools Profiler и функцию “Highlight updates” для подтверждения уменьшения количества рендеров после внесения изменений.

Пример минимальной настройки формы

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
  email: z.string().email(),
  // ... другие поля
});

export function MyForm() {
  const { register, handleSubmit, control } = useForm({
    resolver: zodResolver(schema),
    mode: 'onSubmit', // или 'onTouched'
    defaultValues: { email: '', /* ... */ },
  });

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <input type="email" {...register('email')} />
      {/* другие registered inputs */}
      <button type="submit">Submit</button>
    </form>
  );
}

Этот подход делает ввод текста максимально дешевым — RHF обновляет внутренние refs и DOM, но не вызывает повторный рендер всей формы для каждого символа.

Работа с управляемыми компонентами

Если вам необходимо использовать управляемый компонент (например, React-Select или Material UI TextField), оберните только это поле в Controller. Это ограничит повторные рендеры поддеревом этого компонента.

import { Controller } from 'react-hook-form';

<Controller
  name="country"
  control={control}
  render={({ field }) => (
    <ReactSelect {...field} options={countryOptions} />
  )}
/>

Оптимизация валидации

Валидация не бесплатна. Если вы используете mode: 'onChange или запускаете resolver на каждый keystroke, это вызовет повторную валидацию (и потенциально повторные рендеры) на каждый ввод. Вместо этого:

Профилирование и бенчмарки

В моем бенчмарке форма с 20+ полями и вложенными секциями показала следующие результаты:

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

Когда использовать RHF + Zod

Подход uncontrolled-first с RHF и Zod — это практичное и измеримое улучшение для enterprise-форм. Однако есть и trade-offs:

Если ваши формы начинают тормозить при увеличении масштаба, этот подход — один из самых эффективных способов улучшить производительность. Начните с чеклиста, профилируйте и перемещайте подписки вниз — результаты будут заметны сразу.

Какие узкие места вы обнаружили в своих формах и как их устранили? Делитесь примерами из практики — иногда небольшие изменения могут дать значительный прирост.


Источник: https://dev.to/nainikmehta/uncontrolled-first-react-forms-react-hook-form-zod-1653