Skip to main content
Back to module
React Fundamentals · State and Events

Events and forms

Reading42mLesson 2 of 3 in this module

What you'll learn

  • Build a controlled form
  • Attach event handlers by passing functions to camelCase event props
  • Derive form validity from state and reset fields by setting state
  • Label form fields and announce results accessibly

Handle clicks and typing, and build a controlled form whose state drives validation and submission.

Why it matters

Sign-up pages, search boxes, filters, checkout steps: most of the interaction in an app is people clicking and typing. React handles events with props such as onClick and onChange, and handles form fields with controlled components, where React state is the single source of truth for what is in each input.

Handling events

Pass a function to an event prop. React calls it with an event object:

JSX
function SaveButton() {
  function handleClick(event) {
    console.log("Saved!", event.type); // "click"
  }
  return <button onClick={handleClick}>Save</button>;
}
  • Event props are camelCase: onClick, onChange, onSubmit, onKeyDown, onFocus.
  • Pass the function, do not call it: onClick={handleClick}, not onClick={handleClick()}.
  • To pass arguments, wrap it: onClick={() => removeItem(item.id)}.
  • event.preventDefault() stops the browser's default action (following a link, submitting a form). event.stopPropagation() stops the event bubbling to parent elements.

React's event object wraps the browser's native event and works the same across browsers.

Controlled inputs

In plain HTML an input keeps its own value. In React you usually keep the value in state and feed it back to the input:

JSX
function NameField() {
  const [name, setName] = React.useState("");
  return (
    <label>
      Name
      <input value={name} onChange={(e) => setName(e.target.value)} />
    </label>
  );
}

This loop is the core of controlled components:

  1. The input shows value={name}.
  2. The user types; React fires onChange with the new text in e.target.value.
  3. You call setName, React re-renders, and the input shows the new value.

If you set value without onChange, the input becomes read-only, and React warns you. Because the value lives in state, you can validate, transform (for example e.target.value.toUpperCase()), reset or pre-fill it easily.

In React, onChange fires on every keystroke, like the DOM input event, not only when the field loses focus.

Other field types

JSX
<textarea value={bio} onChange={(e) => setBio(e.target.value)} />

<select value={plan} onChange={(e) => setPlan(e.target.value)}>
  <option value="free">Free</option>
  <option value="pro">Pro</option>
</select>

<input
  type="checkbox"
  checked={agree}
  onChange={(e) => setAgree(e.target.checked)}
/>

Checkboxes use checked and e.target.checked instead of value.

One object for many fields

For larger forms, keep the fields in one object and use the input's name to update the right key:

JSX
const [form, setForm] = React.useState({ name: "", email: "" });

function handleChange(e) {
  const { name, value } = e.target;
  setForm((f) => ({ ...f, [name]: value }));
}

<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />

Validation and submitting

Validation rules are derived from state, so compute them during render:

JSX
const valid = form.name.trim() !== "" && form.email.includes("@");

function handleSubmit(e) {
  e.preventDefault(); // stay on the page
  if (!valid) return;
  onSignup({ name: form.name.trim(), email: form.email.trim() });
  setForm({ name: "", email: "" }); // clear the fields
}

return (
  <form onSubmit={handleSubmit}>
    ...
    <button type="submit" disabled={!valid}>Sign up</button>
  </form>
);

Using a real <form> with onSubmit is best in apps: pressing Enter submits and screen readers understand the structure. (The exercise preview on this site blocks form submission, so the Do tab uses a type="button" with onClick instead.)

Client-side checks are for a pleasant experience only. The server must validate again, because anyone can send requests without your form.

Accessible forms

  • Every input needs a label. Wrap the input in <label> or connect them with htmlFor and id.
  • Show errors as text near the field, not just a red border, and use role="alert" or role="status" for messages that appear after an action so screen readers announce them.
  • A disabled button gives no hint why. For important forms, also show what is missing.

Uncontrolled inputs

Sometimes you only need the value at submit time. You can let the DOM keep it and read it with a ref (React.useRef) or new FormData(e.target). That is an uncontrolled input. Controlled inputs are the default choice when the UI reacts to what the user types: live validation, disabling buttons, filtering a list.

Common mistakes

  • value without onChange (read-only field) or switching an input between value={undefined} and a string (React warns about changing from uncontrolled to controlled). Start with "".
  • Forgetting e.preventDefault() in onSubmit, so the page reloads.
  • Using value on a checkbox instead of checked.
  • Trusting client-side validation as security.

Recap

  • Event props take a function; use arrow functions to pass arguments.
  • Controlled inputs: value from state plus onChange that sets state.
  • Derive validity from state; clear or reset by setting state.
  • Label every field and announce results; validate again on the server.

Try it

Open the Do tab to build a controlled sign-up form whose button enables only when the fields are valid, and that clears itself after a successful sign-up.

Key terms (4)

controlled component
A form field whose value comes from React state and whose onChange handler updates that state.
onChange
The React event prop that fires on every keystroke or change in a field, with the new value in e.target.value.
preventDefault
A method on the event object that stops the browser's default action, such as reloading the page on form submit.
uncontrolled
An input whose value the DOM keeps, read only when needed through a ref or FormData.

Recap

  • Event props take a function; wrap it in an arrow function to pass arguments.
  • Controlled inputs take value from state plus an onChange that sets state; checkboxes use checked.
  • Derive validity from state, and clear or reset fields by setting state.
  • Label every field, announce results, and validate again on the server.
Also in this lesson: