React lets the developer add event handlers to the JSX. Event handlers are custom functions that will be triggered in response to interactions like clicking, hovering, focusing form inputs, and so on.
Adding event handlers
To add an event handler, the developer will first define a function and then pass it as a prop to the appropriate JSX tag. For example, here is a button that does not do anything yet:
The developer can make it show a message when a user clicks by following these three steps:
- Declare a function called
handleClick
inside theButton
component. - Implement the logic inside that function (use
alert
to show the message). - Add
onClick={handleClick}
to the<button>
JSX.
The developer defined the handleClick
function and then passed it as a prop to <button>
. handleClick
is an event handler. Event handler functions:
- are usually defined inside the components.
- have names that start with
handle
, followed by the name of the event.
By convention, it is common to name event handlers as handle
followed by the event name. The developer will often see onClick={handleClick}
, onMouseEnter={handleMouseEnter}
, and so on.
Alternatively, the developer can define an event handler inline in the JSX:
Or, more concisely, using an arrow function:
All of these styles are equivalent. Inline event handlers are convenient for short functions.
Pitfall: Functions passed to event handlers must be passed, not called. For example,
passing a function (correct) | calling a function (incorrect) |
---|---|
<button onClick={handleClick}> | <button onClick={handleClick()}> |
The difference is subtle. In the first example, the
handleClick
function is passed as anonClick
event handler. This tells React to remember it and only call the function when the user clicks on it. In the secon example, the()
at the end ofhandleClick()
fires the function immediately during rendering, without any clicks. This is because JavaScript inside the JSX{
and}
executes right away.When writing code inline, the same pitfall presents itself in a different way:
passing a function (correct) | calling a function (incorrect) |
---|---|
<button onClick={() => alert('...')}> | <button onClick={alert('...')}> |
Passing inline codde like this will not fire on click - it fires every time the component renders:
If the developer wants to define the event handler inline, wrap it in an anonymous function like so:
Rather than executing the code inside with every render, this creates a function to be called later.
In both cases, what the developer wants to pass is a function:
<button onClick={handleClick}
passes thehandleClick
function.<button onClick={() => alert('...')}>
passes the() => alert('...')
function.
Reading props in event handlers
Because event handlers are declared inside of a component, they have access to the component's props. Here is a button that, when clicked, shows an alert with its message
prop:
Passing event handlers as props
Often the developer wants the parent component to specify a child's event handler. Consider buttons: depending on where the developer uses a Button
component, they might want to execute a different function - perhaps one plays a movie and another uploads an image.
To do this, pass a prop the component receives from its parent like so:
Here, the Toolbar
component renders a PlayButton
and an UploadButton
:
PlayButton
passeshandlePlayClick
as theonClick
prop to theButton
inside.UploadButton
passes() => alert('Uploading!')
as theonClick
prop to theButton
inside.
Finally, the Button
component accepts a prop called onClick
. It passes that prop directly the built-in browser <button>
with onClick={onClick}
. This tells React to call the passed function on click.
If using a design system, it is common for components like buttons to contain styling but not specify behavior. Instead, components like PlayButton
and UploadButton
will pass event handlers down.
Naming event handler props
Built-in components like <button>
and <div>
only support browser event names like onClick
. However, when building custom components, the developer can name their event handler props any way they like.
By convention, event handler props should start with on
, followed by a capital letter.
For example, the Button
component's onClick
prop could have been called onSmash
:
In this example, <button onClick={onSmash}>
shows that the browser <button>
(lowercase) still needs a prop called onClick
, but the prop name received by the custom Button
component is up to the developer!
When the component supports multiple interactions, the developer might name event handler props for app-specific concepts. For example, this Toolbar
component receives onPlayMovie
and onUploadImage
event handlers:
Notice how the App
component does not need to know what Toolbar
will do with onPlayMovie
or onUploadImage
. That’s an implementation detail of the Toolbar
. Here, Toolbar
passes them down as onClick
handlers to its Button
s, but it could later also trigger them on a keyboard shortcut. Naming props after app-specific interactions like onPlayMovie
gives the developer the flexibility to change how they’re used later.
Note: Make sure that the developer uses the appropriate HTML tags for the event handlers. For example, to handle clicks, use
<button onClick={handleClick}>
instead of<div onClick={handleClick}>
. Using a real browser<button>
enables built-in browser behaviors like keyboard navigation. If the developer does not like the default browser styling of a button and wants it to look more like a link or a different UI element, they can achieve it with CSS.
Event propagation
Event handlers will also catch events from any children the component might have. React says that an event "bubbles" or "propagates" up the tree: it starts with where the event happened, and then goes up the tree.
This <div>
contains two buttons. Both the <div>
and each button have their own onClick
handlers. Which handlers fires when a button is clicked?
![[Pasted image 20240321233107.png]]
If the user clicks on either button, its onClick
will run first, followed by the parent <div>
’s onClick
. So two messages will appear. If the user clicks the toolbar itself, only the parent <div>
’s onClick
will run.
Pitfall: All events propagate in React except
onScroll
, which only works on the JSX tag the user attaches it to.
Stopping propagation
Event handlers receive an event object as their only argument. By convention, it is usually called e
, which stands for "event". The developer can use the object to read information about the event.
That event object also lets the developer stop the propagation. If the developer wants to prevent an event from reaching parent components, they need to call e.stopPropagation()
like this Button
component does:
When a user clicks on the button:
- React calls the
onClick
handler passed to<button
>. - That handler, defined in
Button
, does the following:- calls
e.stopPropagation()
, preventing the event from bubbling further. - Calls the
onClick
function, which is a prop passed from theToolbar
component.
- calls
- That function, defined in the
Toolbar
component, displays the button's own alert. - Since the propagation was stopped, the parent
<div>
's<onClick>
handler does not run.
As a result of e.stopPropagation()
, clicking on the buttons now only shows a single alert (from the <button>
) rather than the two of them (from the <button>
and the parent toolbar <div>
). Clicking a button is not the same thing as clicking the surrounding toolbar, so stopping the propagation makes sense for this UI.
Deep Dive: Capture Phase Events:
In rare cases, the developer might need to catch all events on child elements, even if they stopped propagation. For example, maybe the developer wants to log every click to analytics, regardless of the propagation logic. The developer can do this by adding
Capture
at the end of the event name:
Each event propagates in three phases:
- It ravels down, call all
onClickCpature
handlers.- It runs the clicked element's
onClick
handler.- It ravels upwards, calling all
onClick
handlers.Capture events are useful for code like routers and analytics, but the developer probably will not use them in application code.
Passing handlers as alternatives to propagation
Notice how this click handler runs a line of code and then calls the onClick
prop passed by the parent:
The developer can add more code to this handler before calling the parent onClick
event handler, too. This pattern provides an alternative to propagation. It lets the child component handle the event, while also letting the parent component specify some additional behavior. Unlike propagation, it is not automatic, But the benefit of this patter is that the developer can clearly follow the whole chain of code that executes as a result of some event.
If the developer relies on propagation and it is difficult to trace which handlers execute and why, try this approach instead.
Preventing default behavior
Some browser events have default behavior associated with them. For example, a <form>
submit event, which happens when a button inside of it is clicked, will reload the whole page by default:
The developer can call e.preventDefault()
on the event object to stop this from happening:
Do not confuse e.stopPropagation
and e.preventDefault
. They are both useful, but are unrelated:
e.stopPropagation()
stops the event handlers attached to the tags above from firing.e.preventDefault()
prevents the default browser behavior for the few events that have it.
Can event handlers have side effects?
Absolutely! Event handlers are the best place for side effects.
Unlike rendering functions, event handlers do not need to be pure, so it is a great place to change something - for example, change an input's value in response to typing, or change a list in response to a button press. However, in order to change some information, the developer first needs some way to store it. In React, this is done using [[2. State - A Component's Memory|state, a component's memory]].