Irbisa · Шпаргалка
Формы и actions
Контролируемые и неконтролируемые поля, React Hook Form, валидация через Zod, actions в React 19, useActionState, useFormStatus, useOptimistic
Чем controlled-инпут отличается от uncontrolled в React?
function Controlled() { const [name, setName] = useState(''); return <input value={name} onChange={(e) => setName(e.target.value)} />;} function Uncontrolled() {…Как прочитать все значения формы при сабмите, не храня каждое поле в стейте?
function SignupForm({ onSave }: { onSave: (v: Record<string, FormDataEntryValue>) => void }) { function handleSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault(); // stop the full page reload const data = new FormData(e.currentTarget); onSave(Object.fromEntries(data)); }…Как сделать controlled чекбокс, группу радиокнопок и
<select>в React?type Plan = 'free' | 'pro'; function Settings() { const [news, setNews] = useState(false); const [plan, setPlan] = useState<Plan>('free'); const [country, setCountry] = useState('kz');…Найдите баг: при вводе в поле в консоли появляется «A component is changing an uncontrolled input to be controlled».
function NameField() { const [name, setName] = useState<string>(); return ( <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Your name" /> );}Почему
<input type="file">в React всегда неконтролируемый и как показать превью выбранного файла и загрузить его?function AvatarUpload() { const [preview, setPreview] = useState<string | null>(null); const [status, setStatus] = useState<'idle' | 'uploading' | 'done' | 'failed'>('idle'); // frees the previous object URL when it changes, and the last one on unmount useEffect(() => () => { if (preview) URL.revokeObjectURL(preview); }, [preview]);…Как валидировать форму в React и когда запускать валидацию: при изменении, при потере фокуса или при сабмите?
Зачем нужен React Hook Form вместо
useStateна каждое поле и как он сокращает ререндеры?import { useForm } from 'react-hook-form'; type Login = { email: string; password: string }; function LoginForm({ onLogin }: { onLogin: (v: Login) => Promise<void> }) { const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<Login>();…Как валидировать форму схемой Zod в React Hook Form и что это даёт?
import { useForm } from 'react-hook-form';import { zodResolver } from '@hookform/resolvers/zod';import { z } from 'zod'; const schema = z .object({…Что такое
Controllerв React Hook Form и когда он нужен вместоregister?import { Controller, useForm } from 'react-hook-form';import Select from 'react-select'; type Option = { value: string; label: string };type Form = { city: Option | null };…Как сделать в React Hook Form форму с динамическим списком полей (добавление и удаление строк)?
import { useFieldArray, useForm } from 'react-hook-form'; type Invoice = { items: { name: string; qty: number }[] }; function InvoiceForm() { const { control, register, handleSubmit } = useForm<Invoice>({…Найдите баг: форма редактирования показывает пустые поля, хотя данные пользователя уже загрузились.
function EditProfile({ userId }: { userId: string }) { const { data: user } = useQuery({ queryKey: ['user', userId], queryFn: () => fetchUser(userId) }); const { register, handleSubmit } = useForm<ProfileValues>({ defaultValues: { name: user?.name ?? '', email: user?.email ?? '' }, }); return ( <form onSubmit={handleSubmit(saveProfile)}> <input {...register('name')} /> <input {...register('email')} /> <button>Save</button> </form> );}Как сделать многошаговую форму (мастер, wizard) и где хранить её состояние и валидацию между шагами?
const signupSchema = z.object({ email: z.email(), password: z.string().min(8), name: z.string().min(1), city: z.string().min(1),});…Как сделать ошибки формы доступными для пользователей скринридеров и клавиатуры?
function EmailField({ error }: { error?: string }) { const id = useId(); const errorId = `${id}-error`; return ( <div>…Что такое Actions в React 19 и что происходит, если передать функцию в
<form action>?function NewsletterForm({ subscribe }: { subscribe: (email: string) => Promise<void> }) { async function action(formData: FormData) { await subscribe(String(formData.get('email'))); // on success React clears the uncontrolled email field }…Что возвращает
useActionStateи как с его помощью показывать ошибки валидации из form action?type State = { error?: string; ok?: boolean }; function RenameForm({ rename }: { rename: (name: string) => Promise<void> }) { const [state, formAction, isPending] = useActionState<State, FormData>( async (_prev, formData) => { const name = String(formData.get('name')).trim();…Найдите баг: кнопка сабмита никогда не переключается на «Saving...», пока выполняется form action.
import { useFormStatus } from 'react-dom'; function CommentForm({ post }: { post: (fd: FormData) => Promise<void> }) { const { pending } = useFormStatus(); return ( <form action={post}> <textarea name="text" /> <button disabled={pending}>{pending ? 'Saving...' : 'Post'}</button> </form> );}Что увидит пользователь в каждом поле после успешного завершения этого form action?
function PostForm({ save }: { save: (fd: FormData) => Promise<void> }) { const [tag, setTag] = useState(''); return ( <form action={save}> <input name="title" defaultValue="Draft" /> <input name="tag" value={tag} onChange={(e) => setTag(e.target.value)} /> <button>Save</button> </form> );}// The user types "Hooks" into title and "react" into tag, then clicks Save.Как работает
useOptimisticи что происходит с оптимистичным значением, если action упал?type Msg = { text: string; sending?: boolean }; function Chat({ messages, send }: { messages: Msg[]; send: (text: string) => Promise<void> }) { const [optimistic, addOptimistic] = useOptimistic( messages, (list: Msg[], text: string) => [...list, { text, sending: true }],…Как защитить форму, работающую с API, от двойного сабмита и ответов, пришедших не в том порядке?
Большая форма тормозит на каждое нажатие клавиши. Как найти причину и исправить?
Когда достаточно form Actions из React 19, а когда всё же стоит взять React Hook Form?