Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Формы и Actions
forms_actions

Формы и Actions

Управляемые поля, FormData, валидация границ, action, useActionState, useFormStatus и оптимистичные изменения.

Открыть лабораториюv0.1.0Запускается локально из публичного репозитория

Формы и Actions

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

#Результат урока

Вы построите доступную форму с типизированной валидацией, pending/error/success состояниями, защитой от повторной отправки и оптимистичным интерфейсом.

До начала достаточно handlers, state modeling, unknown и discriminated unions. В конце должен появиться не конспект, а форма создания заявки с client/server-валидацией и детерминированным восстановлением после отказа.

#1. Нативная форма как базовый контракт

<form> объединяет поля, submit с Enter, browser validation и progressive behavior. Submit-логика должна жить на onSubmit/action, а не только на click кнопки: иначе Enter и другие submitters обходят код. Каждый input получает label и осмысленное name, потому что name определяет ключ FormData.

Рабочая проверка. Отправьте форму Enter из текстового поля и клавиатурой активируйте submit.

Типичная поломка. Собрать форму из div и click-handler без нативной семантики.

#2. Controlled и uncontrolled inputs

Controlled input получает value/checked и синхронный onChange; источник истины находится в React state. Uncontrolled использует defaultValue и DOM до чтения формы. Нельзя переключать один input между режимами: controlled value не должен становиться undefined. Выбор зависит от того, нужен ли React каждый промежуточный символ.

Рабочая проверка. Сделайте controlled поиск с живым preview и uncontrolled поле комментария, читаемое при submit.

Типичная поломка. Передать value={maybeString} и получить переход controlled/uncontrolled.

#3. FormData — недоверенная граница

FormData.get возвращает строку, File или null. Числа, даты и boolean не восстанавливаются автоматически. На клиенте и сервере вход нужно разобрать и проверить; TypeScript-тип action не защищает от вручную сформированного HTTP-запроса. Ошибки схемы должны быть доменным результатом, системные сбои — отдельной error policy.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

const rawTitle = formData.get('title'); if (typeof rawTitle !== 'string' || rawTitle.trim().length < 3) { return { status: 'invalid' as const, fields: { title: 'Минимум 3 символа' } }; }

Проверка одновременно исключает null/File и применяет доменное правило. Сервер повторяет доверенную валидацию независимо от клиента.

Рабочая проверка. Отправьте отсутствующее поле, File вместо строки и пробельное название.

Типичная поломка. Писать formData.get("title") as string без проверки.

#4. Actions и pending-жизненный цикл

React 19 позволяет передать функцию в action формы. Async Action управляет transition отправки; после успешной отправки uncontrolled form reset происходит автоматически. Action может быть клиентской или Server Function в поддерживающем framework. Она не отменяет необходимость авторизации, идемпотентности и обработки системных ошибок.

Рабочая проверка. Реализуйте action с успешным результатом, validation failure и сетевым отказом.

Типичная поломка. Считать любую функцию с именем action доверенной серверной транзакцией.

#5. useActionState и типизированный результат

useActionState связывает Action с последним возвращённым state и pending-флагом. Функция получает previous state первым аргументом, затем payload. Результат удобно моделировать union: idle | invalid | success | failure. Не возвращайте exception object или внутренние детали сервера пользователю.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

type FormState = | { status: 'idle' } | { status: 'invalid'; fields: Record<string, string> } | { status: 'success'; id: string }; const [state, submitAction, pending] = useActionState(saveRequest, { status: 'idle' });

Union заставляет JSX явно разобрать каждый пользовательский исход, а pending описывает выполняющуюся Action отдельно от последнего результата.

Рабочая проверка. Свяжите field error с aria-describedby и оставьте пользовательский ввод после validation failure.

Типичная поломка. Использовать string | null одновременно для успеха, ошибки и отсутствия результата.

#6. useFormStatus у потомка формы

useFormStatus читает статус родительской формы, как context. Hook должен вызываться в компоненте, отрендеренном внутри form; тот же компонент, который возвращает form, не читает статус собственной будущей формы. Статус содержит pending и данные текущей отправки, но пользовательский текст и live validation не стоит строить на постоянном чтении FormData.

Следующий фрагмент показывает минимальный рабочий контракт, а не заготовку для слепого копирования.

function SubmitButton() { const { pending } = useFormStatus(); return <button disabled={pending}>{pending ? 'Сохраняем…' : 'Сохранить'}</button>; }

Кнопка находится внутри <form action={...}>, поэтому получает статус ближайшей формы без отдельного prop drilling.

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

Типичная поломка. Вызвать useFormStatus рядом с JSX form и ожидать статус этой же формы.

#7. Оптимистичное состояние и согласование

useOptimistic позволяет показать предполагаемый результат на время Action. Transformation должна быть чистой. Каждая optimistic запись получает стабильный client id и статус, чтобы подтверждение сопоставило её с server id, а отказ дал откат или возможность повтора. Нельзя обещать необратимый успех до подтверждения.

Рабочая проверка. Смоделируйте два параллельных optimistic добавления и ответы сервера в обратном порядке.

Типичная поломка. Сопоставлять подтверждение по индексу списка или тексту записи.

#8. Повторная отправка, идемпотентность и ошибки

Disabled submit уменьшает случайные повторы, но не заменяет идемпотентность сервера: запрос можно повторить сетью, вкладкой или атакующим клиентом. Для create-команд нужен idempotency key или доменная дедупликация. Validation error показывается рядом с полем; системный отказ сохраняет данные и даёт безопасный retry. Не все ошибки следует превращать в «неверные поля».

Рабочая проверка. Дважды отправьте одну команду с тем же idempotency key и проверьте один server result.

Типичная поломка. Считать disabled={pending} гарантией exactly-once.

#Лаборатория

Реализуйте форму заявки через React 19 action. Прочитайте FormData как unknown, провалидируйте поля, верните field errors через useActionState, покажите pending через useFormStatus, добавьте optimistic запись с временным id и откат при отказе.

Критерий завершения: форма работает с клавиатуры, повторная отправка контролируется, ошибки связаны с полями, а optimistic state согласуется с подтверждённым результатом. Сначала зафиксируйте наблюдаемое поведение тестом или измерением, затем меняйте реализацию.

#Источники для сверки

  • React input reference
  • React form reference
  • React useActionState
  • React useOptimistic

Устранение неисправностей

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Проектирование API компонентов