157: TypeScript - React Components and Forms
Learning outcomes
- type props, events, refs, state, reducers, and custom hooks;
- keep browser input values separate from parsed domain values;
- avoid
anyat component boundaries.
Study
DOM events contain strings and nullable elements; domain commands may require numbers, dates, or validated unions. Parse at the form boundary. Component props describe what a component needs, while state describes what the UI can actually be in.
Practice
Convert the task editor to TypeScript. Type controlled inputs, submit events, reducer actions, loading/error states, and the API response parser. Add tests for invalid input and for rejecting an unparsed string as a task ID.
Worked example
Keep the input representation and domain representation separate:
function TaskForm({ onSubmit }: { onSubmit: (title: string) => void }) {
const [title, setTitle] = useState('')
function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
const value = title.trim()
if (value) onSubmit(value)
}
return <form onSubmit={submit}><label>Title <input value={title} onChange={(event) => setTitle(event.target.value)} /></label></form>
}
The event type describes the browser event. It does not prove that the title satisfies server rules. The callback is the narrower component contract.
Edge cases
Test whitespace-only input, submit by keyboard, a rejected request, unmount during a pending request, and a server response missing id. Prefer an explicit reducer action union when the form has more than one asynchronous transition.
Checkpoint
Explain why ChangeEvent<HTMLInputElement> is not a domain model and why a reducer action union is safer than { type: string; payload?: any }.
