Jamie Balfour

Welcome to my personal website.

Find out more about me, my personal projects, reviews, courses and much more here.

Part 5.1Building forms with controlled inputs

Controlled inputs

A controlled input receives its value from state and updates that state in onChange. React state becomes the source of truth for what the form displays.

JSX
function TaskForm({ onAdd }) {
  const [title, setTitle] = useState("");

  function handleSubmit(event) {
    event.preventDefault();
    const cleanTitle = title.trim();
    if (!cleanTitle) return;
    onAdd(cleanTitle);
    setTitle("");
  }

  return <form onSubmit={handleSubmit}>
    <label htmlFor="task-title">
      New task
    </label>
    <input
      id="task-title"
      className="task-form__input"
      value={title}
      onChange={(event) =>
        setTitle(event.target.value)
      }
    />
    <button className="button button--primary">
      Add task
    </button>
  </form>;
}

Submitting the form

Handle the form's onSubmit, not only the button click. This supports keyboard submission and keeps the behaviour attached to the correct semantic element.

Validation

Use native constraints such as required and maxLength where possible, then add clear application-specific feedback. Keep an error close to its field and connect it with aria-describedby.

Follow the form flow

  1. The input displays the current title state through its value prop.
  2. Typing fires onChange; the handler reads the browser's new value and updates state.
  3. The component renders again, so the input receives that new value.
  4. Submission prevents the browser's normal page reload, cleans the text and calls the parent's onAdd.
  5. Clearing state renders the input with an empty value.

Do not switch an input between controlled and uncontrolled modes. If its value is controlled, initialise text state to an empty string rather than undefined.

Challenge: add validation feedback

Reject titles shorter than three characters. Show a message beneath the input only after submission fails, connect it to the input with aria-describedby and remove it after a valid submission.

Extension

Add a controlled priority select and pass both title and priority to onAdd.

Feedback 👍
Comments are sent via email to me.