Learn / Frameworks / React / Events, Lists and Forms

React · Lesson 4 of 7

Events, Lists and Forms

Handle events, render lists with keys and build controlled forms.

  • Beginner
  • 17 min read
  • 3 objectives

Before this lessonLesson 3: State with useState

What you will learn

  • Handle events
  • Render lists with keys
  • Build a controlled form

Interfaces are interactive: users click, type and submit. In React you attach handlers with camelCase props such as onClick, onChange and onSubmit, and pass them a function, not the result of calling one.

Handling events

function Save() {
  function handleClick(event) {
    console.log("saved", event.type);
  }
  return <button onClick={handleClick}>Save</button>;
  // Wrong: onClick={handleClick()}  runs immediately on every render
}

Rendering lists

Use map to turn an array into elements. Each item needs a stable, unique key prop so React can tell which item is which when the list changes. Use an id from your data, not the array index, if the list can be reordered or filtered.

const todos = [
  { id: 1, text: "Learn React", done: true },
  { id: 2, text: "Build an app", done: false },
];

function TodoList() {
  return (
    <ul>
      {todos.map((t) => (
        <li key={t.id} style={{ textDecoration: t.done ? "line-through" : "none" }}>
          {t.text}
        </li>
      ))}
    </ul>
  );
}

Controlled inputs

In a controlled input, React state is the single source of truth: the input shows value from state and updates it in onChange. That makes validation, formatting and clearing trivial.

function NameForm() {
  const [name, setName] = useState("");
  return (
    <label>
      Name
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>Hello, {name || "stranger"}</p>
    </label>
  );
}

A complete form

function AddTodo({ onAdd }) {
  const [text, setText] = useState("");

  function handleSubmit(e) {
    e.preventDefault();               // stop the page reload
    if (!text.trim()) return;
    onAdd(text.trim());
    setText("");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={text} onChange={(e) => setText(e.target.value)} placeholder="New task" />
      <button type="submit">Add</button>
    </form>
  );
}

function App() {
  const [todos, setTodos] = useState([]);
  return (
    <>
      <AddTodo onAdd={(t) => setTodos([...todos, { id: crypto.randomUUID(), text: t }])} />
      <ul>{todos.map((t) => <li key={t.id}>{t.text}</li>)}</ul>
    </>
  );
}

Checkboxes use checked and e.target.checked; selects use value like text inputs. For forms with many fields, store them in one object: setForm({ ...form, [e.target.name]: e.target.value }).

// Write your solution here
Up next · Lesson 5Effects with useEffectSynchronize with the outside world: fetching, timers and subscriptions.