webroad.online
  1. 1Веб
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Инструменты
  8. 8React
  9. 9Стейт-менеджмент
  10. 10Next.js
  11. 11Формы
  12. 12Данные и бэкенд
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Анимации
  16. 16Тестирование
  17. 17Архитектура
Формы · Урок 1 из 4

Формы в React

Контролируемые и неконтролируемые, FormData, useActionState, useFormStatus, доступные ошибки.

Обновлено

Что значит форма в React

У формы три вещи, которыми нужно управлять: значения полей, ошибки валидации и состояние отправки (pending, успех, ошибка сервера). React даёт два базовых способа хранить значения, и все библиотеки форм построены на одном из них.

База HTML (name, label, типы полей, нативная валидация) — в уроке Формы HTML; здесь мы строим поверх неё.

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

Разница между контролируемым и неконтролируемым полем:

Контролируемое Неконтролируемое
Где хранится значение в состоянии React (useState) в DOM (поле хранит его само)
Как его прочитать из переменной, в любой момент при отправке: FormData или ref
Перерисовка на каждое нажатие да нет
Код value + onChange на каждом поле только name (+ defaultValue)
Когда значение влияет на интерфейс в реальном времени: живой поиск, зависимые поля, форматирование (телефон, карта) большинство форм: вход, контакты, настройки
// контролируемое
const [email, setEmail] = useState('')
<input value={email} onChange={e => setEmail(e.target.value)} />

// неконтролируемое
<input name="email" defaultValue={user.email} />

value без onChange = заблокированное поле (только чтение). defaultValue = начальное значение, дальше управляет DOM.

Чтение данных: FormData

function onSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault()
  const form = new FormData(e.currentTarget)
  const email = String(form.get('email'))
  const tags = form.getAll('tags')            // чекбоксы с одним name → массив
  const data = Object.fromEntries(form)       // ⚠️ для повторяющихся name сохраняет только последнее значение
}

Значения FormData — всегда строки (или File). Отмеченный чекбокс отправляет 'on', неотмеченный не появляется вообще.

React 19: формы с действиями

React 19 позволяет передать функцию прямо в action. React сам займётся preventDefault, FormData и состоянием ожидания.

'use client'
import { useActionState } from 'react'
import { useFormStatus } from 'react-dom'

type State = { error?: string; ok?: boolean }

async function subscribe(prev: State, formData: FormData): Promise<State> {
  const email = String(formData.get('email') ?? '')
  if (!email.includes('@')) return { error: 'Неверный email' }
  await api('/subscribe', { method: 'POST', body: formData })
  return { ok: true }
}

function SubmitButton() {
  const { pending } = useFormStatus()          // читает состояние родительской формы
  return <button disabled={pending}>{pending ? 'Отправка…' : 'Подписаться'}</button>
}

export function Newsletter() {
  const [state, action] = useActionState(subscribe, {})
  return (
    <form action={action}>
      <input name="email" type="email" required />
      {state.error && <p role="alert">{state.error}</p>}
      <SubmitButton />
    </form>
  )
}
Хук Что даёт Где
useActionState(fn, initial) [state, action, isPending] — результат последней отправки компонент с <form>
useFormStatus() { pending, data } родительской формы дочерний компонент формы
useOptimistic интерфейс, обновлённый до ответа списки, лайки

С Server Action вместо клиентской функции та же форма работает даже без JavaScript.

Доступные ошибки

Ошибку нужно видеть и слышать:

<label htmlFor="email">Email</label>
<input id="email" name="email" aria-invalid={!!error} aria-describedby={error ? 'email-error' : undefined} />
{error && <p id="email-error" role="alert">{error}</p>}
Атрибут Роль
aria-invalid скринридер объявляет «invalid»; стилизуете через aria-invalid:border-danger
aria-describedby связывает сообщение с полем — его прочитают, когда пользователь дойдёт до поля
role="alert" / aria-live сообщение объявляется сразу при появлении

Когда нужна библиотека

В форме... Без библиотеки С библиотекой (React Hook Form / Conform)
2–5 полей, простая валидация ✓ useActionState + FormData не нужно
валидация при blur / вводе, сообщения по полям громоздко ✓
динамические списки (добавить / удалить строки) громоздко ✓
многошаговый мастер, зависимые поля громоздко ✓

Коротко

  • Неконтролируемые + FormData для большинства форм; контролируемые — когда значение меняет интерфейс вживую.
  • React 19: action={fn}, useActionState для результата, useFormStatus для ожидания.
  • Ошибки: aria-invalid + aria-describedby + role="alert".

Официальные источники

Упражнения

Была ли страница полезной?

Один клик — без регистрации.