Lesson 6 of 31
Handling Events
Attaching event handlers in JSX, the SyntheticEvent wrapper, passing arguments to a handler, and why event handling is a normal JavaScript function underneath.
Interactivity starts with responding to what the user does — clicks, typing, submitting a form. React's event handling looks close to plain DOM events, with a few deliberate differences worth understanding.
Attaching a handler
Event props follow a onEventName naming convention, camelCased, and take a
function:
function LikeButton() {
function handleClick() {
console.log("Liked!");
}
return <button onClick={handleClick}>Like</button>;
}The critical detail: onClick={handleClick} passes the function itself —
React calls it later, when the click actually happens. Writing
onClick={handleClick()} instead calls the function immediately, during
render, and passes whatever it returns (often undefined) as the handler.
This is one of the most common early mistakes, and it fails silently — no
error, just a button that does nothing on click and already ran its "handler"
once on every render.
Passing arguments
A named handler function takes no arguments by default beyond the event object React passes automatically. To pass your own data — which item was clicked, for instance — wrap the call in an inline arrow function:
function TrackList({ tracks, onDelete }) {
return (
<ul>
{tracks.map((track) => (
<li key={track.id}>
{track.title}
<button onClick={() => onDelete(track.id)}>Remove</button>
</li>
))}
</ul>
);
}The arrow function () => onDelete(track.id) is itself the thing React
calls on click; it just happens to call onDelete with an argument from the
surrounding closure when it runs.
The event object
Handlers receive an event object as their argument, same as vanilla DOM code:
function SearchForm({ onSearch }) {
function handleSubmit(event) {
event.preventDefault(); // stop the page from reloading
const query = new FormData(event.target).get("query");
onSearch(query);
}
return (
<form onSubmit={handleSubmit}>
<input name="query" />
<button type="submit">Search</button>
</form>
);
}React calls this a SyntheticEvent — a cross-browser wrapper around the
underlying native event, exposing a consistent API (preventDefault,
stopPropagation, target, and so on) regardless of which browser is
running the code. Historically (before React 17) these objects were
pooled and reused for performance, which meant accessing event properties
asynchronously (after an await, for example) could read stale, nulled-out
data unless you called event.persist() first. React 17 removed event
pooling entirely, so in current React the event behaves like a normal object
you can reference asynchronously without special handling — a real
difference to know if you ever read older React code or tutorials that
mention persist().
Where the listener actually lives
Another React 17 change worth knowing, purely for reading older material:
React attaches one delegated event listener per event type to the root
container your app renders into, rather than to individual DOM nodes.
Before React 17, that delegation point was document itself; React 17
moved it to the root container. Practically, this rarely changes how you
write handlers — it mostly matters if an app mixes multiple independent
React roots (or React versions) on the same page, since events no longer
bubble past a root's own container into a document-level listener
belonging to a different root.
Try it yourself
A small form using both a click handler with a passed argument and a submit handler that reads the event:
What to remember
- Pass the function itself to an event prop (
onClick={handleClick}), never call it (onClick={handleClick()}). - Wrap a handler in an inline arrow function to pass your own arguments alongside the event.
- The event object is a SyntheticEvent — a cross-browser wrapper with a
preventDefault/stopPropagationAPI; since React 17 it is no longer pooled, so it's safe to reference asynchronously. - Modern React delegates listeners to the root container it renders into, not to
documentor to each element individually.
Check yourself
4 questions · pass 3/4 to unlock The Virtual DOM and Reconciliation
1.What's wrong with
<button onClick={handleClick()}>Save</button>?2.How do you pass an argument to an event handler, like which item was clicked?
3.What is a React SyntheticEvent?
4.Where does React actually attach its event listeners in modern versions (React 17+)?
4 left to answer