Irbisa · Cheatsheet
Forms & Actions
Controlled and uncontrolled inputs, React Hook Form, schema validation with Zod, React 19 actions, useActionState, useFormStatus, useOptimistic
What is the difference between a controlled and an uncontrolled input in React?
function Controlled() { const [name, setName] = useState(''); return <input value={name} onChange={(e) => setName(e.target.value)} />;} function Uncontrolled() {…How do you read all form values on submit without keeping each field in state?
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)); }…How do you make a checkbox, a radio group and a
<select>controlled in React?type Plan = 'free' | 'pro'; function Settings() { const [news, setNews] = useState(false); const [plan, setPlan] = useState<Plan>('free'); const [country, setCountry] = useState('kz');…Find the bug: typing into the field logs "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" /> );}Why is
<input type="file">always uncontrolled in React, and how do you preview and upload the selected file?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]);…How do you validate a form in React, and when should validation run: on change, on blur or on submit?
Why use React Hook Form instead of
useStatefor every field, and how does it keep re-renders low?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>();…How do you validate a form with a Zod schema in React Hook Form, and what do you gain from it?
import { useForm } from 'react-hook-form';import { zodResolver } from '@hookform/resolvers/zod';import { z } from 'zod'; const schema = z .object({…What is
Controllerin React Hook Form and when do you need it instead ofregister?import { Controller, useForm } from 'react-hook-form';import Select from 'react-select'; type Option = { value: string; label: string };type Form = { city: Option | null };…How do you build a form with a dynamic list of fields (add and remove rows) in 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>({…Find the bug: the edit form shows empty fields although the user data has already loaded.
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> );}How do you build a multi-step form (a wizard), and where do you keep its state and validation between steps?
const signupSchema = z.object({ email: z.email(), password: z.string().min(8), name: z.string().min(1), city: z.string().min(1),});…How do you make form errors accessible to screen reader and keyboard users?
function EmailField({ error }: { error?: string }) { const id = useId(); const errorId = `${id}-error`; return ( <div>…What are Actions in React 19, and what happens when you pass a function to
<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 }…What does
useActionStatereturn, and how do you use it to show validation errors from a 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();…Find the bug: the submit button never switches to "Saving..." while the form action is running.
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> );}What does the user see in each field after this form action finishes successfully?
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.How does
useOptimisticwork, and what happens to the optimistic value if the action fails?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 }],…How do you prevent double submits and out-of-order responses in a form that talks to an API?
A large form lags on every keystroke. How do you find the cause and fix it?
When are React 19 form Actions enough, and when would you still pick React Hook Form?