Skip to content
WANCODE
BM

Event Handling

16 minIntermediateReact

In this lesson

  • Attach handlers with onClick and onChange
  • Tell the difference between passing a function (onClick={fn}) and calling it (onClick={fn()})
  • Pass event handlers down to child components as props
  • Read e.target.value and build a controlled input

The big idea

In React you don't call addEventListener. You pass a function as a prop like onClick, React calls it when the event happens, and your handler usually updates state.

In plain JavaScript you found an element and attached a listener. In React, you describe the listener right in the JSX, next to the element it belongs to. Event props are camelCase: onClick, onChange, onSubmit, onKeyDown.

Plain DOM

JavaScript
const btn = document.querySelector("#plus");
btn.addEventListener("click", () => {
  cups = cups + 1;
  render(); // you redraw by hand
});

React

JSX
function addCup() {
  setCups(cups + 1); // React re-renders
}

<button onClick={addCup}>+</button>

Pass the function — don't call it

This is the most common React event bug. Remember that { } runs JavaScript while rendering. onClick={addCup} hands React the function to call later. onClick={addCup()} calls it right now, during render, and gives React whatever it returns (usually undefined). If that function sets state, you get an endless loop and React stops with "Too many re-renders".

Called during render

JSX
<button onClick={addCup()}>+</button>

<button onClick={setDrink("Kopi O")}>
  Kopi O
</button>

Called on click

JSX
<button onClick={addCup}>+</button>

<button onClick={() => setDrink("Kopi O")}>
  Kopi O
</button>

Need to pass an argument? Wrap the call in an arrow function: () => setDrink("Kopi O"). The arrow is created now; the call inside it runs on click.

The event object

React calls your handler with an event object — a wrapper around the browser's native event with the same familiar parts: e.target (the element), e.target.value (an input's current text), e.key, e.preventDefault().

JSX
function handleChange(e) {
  console.log(e.target.value); // what's in the box right now
}

<input onChange={handleChange} />

In React, onChange fires on every keystroke (like the DOM's input event), not only when the box loses focus.

Handlers as props

A child component often shouldn't own the state — the parent does. So the parent passes a function down as a prop, and the child calls it. By convention, the prop is named onSomething and the function handleSomething.

JSX
function DrinkButton({ label, onPick }) {
  return <button onClick={onPick}>{label}</button>;
}

// in the parent:
<DrinkButton label="Kopi O" onPick={() => setDrink("Kopi O")} />

Preview: controlled inputs

Give an input both value={note} and an onChange that updates note, and React state becomes the single source of truth for what's in the box. That's a controlled input. Because the value comes from state, clearing the state clears the box too. You'll use this pattern for every form later in the track.

value without onChange makes a read-only box: typing does nothing, and React warns in the console. Always pair them.

The + and - buttons do nothing yet. Connect them to addCup and removeCup with onClick — pass the functions, don't call them.

Then make sure the order can't go below 1 cup: change removeCup so it only decreases when cups is more than 1.

Checks

  • + adds a cup
  • - removes a cup
  • It never goes below 1

Check understanding

Q1

What happens with <button onClick={save()}>Save</button>?

Q2

In onChange={(e) => setName(e.target.value)}, what is e.target.value?

Q3

What makes an input "controlled"?

What you learned

  • Event props are camelCase (onClick, onChange) and take a function.
  • onClick={fn} passes it; onClick={fn()} calls it during render. Need arguments? onClick={() => fn(x)}.
  • Parents pass handlers down as onSomething props; children call them.
  • value + onChange (reading e.target.value) make a controlled input driven by state.
Preview

Console output from your code appears here.

Step 1 of 3