Good Code
The good version keeps the input value in React state, so validation, submit behavior, and reset behavior all use the same value.
Lesson 05
Keep form values in React state when the UI needs validation, reset, or submit logic.
import { type FormEvent, useState } from "react";
type ReviewTitleFormProps = {
onCreate: (title: string) => void;
};
export function ReviewTitleForm({ onCreate }: ReviewTitleFormProps) {
const [title, setTitle] = useState("");
function handleSubmit(event: FormEvent<HTMLFormElement>) {
// React state is the single source for validation and reset.
event.preventDefault();
const trimmedTitle = title.trim();
if (!trimmedTitle) {
return;
}
onCreate(trimmedTitle);
setTitle("");
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="review-title">Review title</label>
<input
id="review-title"
value={title}
onChange={(event) => setTitle(event.target.value)}
/>
<button type="submit">Create review</button>
</form>
);
}import type { FormEvent } from "react";
type ReviewTitleFormProps = {
onCreate: (title: string) => void;
};
export function ReviewTitleForm({ onCreate }: ReviewTitleFormProps) {
function handleSubmit(event: FormEvent<HTMLFormElement>) {
// DOM queries bypass React's form state and testing surface.
event.preventDefault();
const input = document.querySelector<HTMLInputElement>("#review-title");
const title = input?.value.trim();
if (title) {
onCreate(title);
input.value = "";
}
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="review-title">Review title</label>
<input id="review-title" defaultValue="" />
<button type="submit">Create review</button>
</form>
);
}The good version keeps the input value in React state, so validation, submit behavior, and reset behavior all use the same value.
The bad version reaches into the DOM to read and mutate the input. That bypasses React's data flow and makes the form harder to test or extend.