Module: TypeScript
TypeScript·157·4 MIN READ

157: TypeScript - React Components and Forms

TOPICS COVERED: 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 any at 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:

tsx
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 }.

References