Chapter 7 of 20 · React for Beginners — Complete Free Tutorial

Handling Events in React

Interactive screens in the React for Beginners series respond to clicks, keystrokes and submissions. React wraps the browser's event system in a familiar but slightly different API: camelCase handler props, functions passed by reference, and a synthetic event object that works identically across browsers. This chapter covers the patterns you will use in every application.

onClick and the camelCase rule

HTML attributes are lowercase (onclick); JSX props are camelCase (onClick). You pass a function reference, not a string and not the result of a call:

function LikeButton() {
  function handleClick() {
    console.log('Liked!');
  }

  return <button onClick={handleClick}>Like</button>;
}

Expected output: clicking the button prints Liked! to the console — once per click. The markup stays clean because the logic lives in a named function above it.

Pass, don't call

The single most common beginner bug is invoking the handler during render:

SyntaxWhat happensVerdict
onClick={handleClick}React receives the function and calls it on click✅ Correct
onClick={handleClick()}Runs during render; returns whatever the function returns (often undefined)❌ Wrong
onClick="handleClick()"Passes a string; nothing callable happens❌ Wrong

Need arguments? Wrap the call in an arrow function so it runs only when clicked: onClick={() => handleClick(id)}. This creates a new function each render, which is fine for ordinary buttons.

The event object

Handlers receive one argument: React's synthetic event, carrying the useful parts of the native event with cross-browser consistency. You get currentTarget, target, key, preventDefault() and more:

function SubmitForm() {
  function onSubmit(event) {
    event.preventDefault();          // stop the page from reloading
    const data = new FormData(event.target);
    console.log('Sending', data.get('email'));
  }

  return (
    <form onSubmit={onSubmit}>
      <input name="email" type="email" required />
      <button type="submit">Join</button>
    </form>
  );
}

Expected output: submitting prints Sending ada@example.com and the browser stays on the page — without preventDefault() a form submit reloads the document, which is exactly what plain DOM forms taught you to prevent.

Common events

Almost every event you know has a camelCase JSX twin. The ones you will type most:

EventFires whenTypical use
onClickPointer clickButtons, toggles, selection
onChangeInput value changes (on every keystroke)Controlled form fields
onSubmitForm submittedLogin, search, contact forms
onKeyDownKey pressedShortcuts, Enter-to-submit
onMouseEnter / onFocusPointer enters / element focusedTooltips, validation hints

Unlike HTML's on* string attributes, these props take real functions, so you can share one handler between many elements.

Updating state from events

Events are where state setters belong. Combining this chapter with the last one gives the standard loop — user acts, handler runs, state updates, React re-renders:

const [items, setItems] = useState([]);

function onAdd(name) {
  setItems((prev) => [...prev, { id: crypto.randomUUID(), name }]);
}

function onRemove(id) {
  setItems((prev) => prev.filter((item) => item.id !== id));
}

Expected output: calling onAdd('Vite') grows the list; onRemove(theId) shrinks it — each change producing exactly one re-render with the current list.

Event bubbling and stopPropagation

Events bubble from child to parent, just as in the DOM. If an inner control must not trigger its parent (a delete button inside a clickable card), call event.stopPropagation() inside the inner handler. Prefer restructuring the markup when you can — nesting interactive elements inside another interactive element is also an accessibility problem. React wires one listener per event at the root and dispatches through your component tree, so synthetic bubbling follows the same order you learned with native events: target handlers run first, then ancestors.

One detail differs from old React: there is no event pooling anymore, so you can safely read event.target.value asynchronously — inside a setTimeout or an awaited fetch — without the object being nulled out.

Common mistakes

  • Invoking the handler in JSX — {save()} runs on every render; pass {save} or wrap it: () => save(id).
  • Wrong letter case — onclick and onchange are silently ignored by React.
  • Forgetting preventDefault() on forms — the page reloads and your state vanishes.
  • Reading state right after the setter — the variable still holds the previous value until the next render.
  • Stale closures in long-lived handlers — if a timer or subscription captures state, use the functional updater form or an effect (chapter 12).

Practice checklist

  1. Add a working handleClick to the Like button and confirm one log per click.
  2. Write the broken onClick={handleClick()} version, observe the immediate console noise, then fix it.
  3. Build a form that logs its values with FormData and stops the browser reload.
  4. Add an input that echoes what you type by writing its value into state.
  5. Make Enter in the search input submit the form (onKeyDown or a submit button).

Events give you state to change — but what should the UI show while that state is one thing or another? That is Conditional Rendering.

Quiz: Handling Events in React

5 questions · 70% to pass

Frequently Asked Questions

React is a JavaScript library for building user interfaces from reusable components. It manages how your UI updates when data changes, so you describe what the screen should look like and React performs the efficient DOM updates. It is one of the most in-demand frontend skills and powers products from Netflix to Instagram, which makes it an excellent next step after core JavaScript.

You should be comfortable with functions, arrays and objects (map, filter, spread), destructuring, modules and basic async/await. React itself is written in plain JavaScript — JSX is a syntax layer on top. If any of those feel shaky, revisit the JavaScript fundamentals first and this series will go much faster.

React is a library focused only on the UI layer — you choose your own router, data layer and tooling. Angular is a full framework that ships routing, forms, HTTP client and testing conventions as one opinionated package. React's flexibility and huge ecosystem make it the common choice for teams that want to compose their own stack.

A component is a reusable function that returns UI. It receives input through props, keeps private data in state, and can render other components. Pages are composed by nesting small components (navbar, card list, footer) instead of one giant file, which keeps code testable and easy to change.

JSX is a syntax extension that looks like HTML but compiles to React's create() calls — it is a description of UI, not markup the browser parses. Small differences matter: class becomes className, style takes an object, and you embed JavaScript expressions inside curly braces. Once compiled, React turns that description into real DOM nodes.

Props are inputs passed from a parent — they are read-only inside the child, like function arguments. State is private, mutable data owned by a component; updating it triggers a re-render. The rule of thumb: values that come from outside the component are props; values that change over the component's lifetime are state.

Rendering means calling your component function to produce a description of the UI for the current state and props. It does not always mean DOM work — React compares the new description with the previous one (reconciliation) and applies only the differences to the real DOM. This is why updating state 100 times does not mean 100 full page rebuilds.

An effect runs after the component has committed to the screen — after render, not during it. You list dependencies in an array, and React re-runs the effect only when one of them changes. If the effect returns a cleanup function, React runs it before the next re-run and on unmount, which is where you cancel timers, abort fetches and remove listeners.

Use Vite. Create React App is effectively in maintenance mode, while Vite starts instantly (native ES modules) and builds fast, and it is the default choice for new React projects. This series uses Vite for every example — you only need Node.js installed to follow along.

React apps compile to static files (HTML, CSS and JavaScript), so any static host works: Netlify, Vercel, GitHub Pages or Cloudflare Pages. Run the production build, upload the dist folder, and configure a single-page-application fallback so unknown paths load index.html — routing chapters explain why that fallback is required.