Event Capturing and Bubbling in React
Elad Tzemach
Posted on December 10, 2020
Imagine you have the following code:
const [counter, setCounter] = useState(0);
return (
<div onClick={() => setCounter((prevCounter) => prevCounter + 1)}>
<button onClick={() => setCounter((prevCounter) => prevCounter + 1)}>
Counter value is: {counter}
</button>
</div>
);
That renders this button:
What would be displayed on that button if you click it?
If you guessed "Counter value is: 1", you were wrong!
What we get is this:
But why?
Understanding Event Propagation
In our example, although we clicked on the button
, the event handler of its div
parent was triggered as well. That's because events don't just affect the target element that generated the eventโthey travel up and down through the DOM tree to reach their target.
This is known as event propagation: a mechanism that defines how events propagate or travel through the DOM tree to arrive at its target and what happens to it afterward.
The concept of event propagation was introduced to deal with the situations in which multiple elements in the DOM hierarchy with a parent-child relationship have event handlers for the same event, such as a mouse click. Now, the question is which element's click event will be handled first when the user clicks on the inner element: the click event of the outer element, or the inner element?
Event Propagation has three phases:
- Capturing Phase - the event starts from the
window
down until it reaches theevent.target
. - Target Phase - the event has reached the
event.target
. The most deeply nested element that caused the event is called a target element, accessible asevent.target
. - Bubbling Phase - the event bubbles up from the
event.target
element up until it reaches thewindow
, meaning: when an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors. That's the reverse of what is happening in the Capturing Phase.
Event Bubbling and Capturing in React
Bubbling and capturing are both supported by React in the same way as described by the DOM spec, except for how you go about attaching handlers.
Bubbling is as straightforward as with the normal DOM API; simply attach a handler to an eventual parent of an element, and any events triggered on that element will bubble to the parent, just like in our example in the beginning.
Capturing is just as straightforward, but instead of the onClick
prop, you have to use onClickCapture
on your element.
How Do You Stop an Event from Bubbling/Capturing?
Going back to our original example, how can we make sure our counter is only incremented by 1 when we click the button?
The answer is using stopPropagation()
This method of the Event
interface prevents further propagation of the current event in the capturing and bubbling phases.
It does not, however, prevent any default behaviors from occurring. (if you want to stop those behaviors, you would need to use the preventDefault()
method)
If we change our code to:
const [counter, setCounter] = useState(0);
return (
<div onClick={() => setCounter((prevCounter) => prevCounter + 1)}>
<button
onClick={(event) => {
event.stopPropagation();
setCounter((prevCounter) => {
return prevCounter + 1;
});
}}
>
Counter value is: {counter}
</button>
</div>
Our counter will be incremented by 1 each time we click the button, thanks to event.stopPropagation()
which prevents the event from bubbling up to button
's parent and triggering the parent's onClick
as well.
However, be careful when stopping events from propagating, because sometimes you canโt really be sure you wonโt be needing the event above in one of the element's parents, maybe for completely different things.
If this is the case, one alternative to stopping the propagation is writing your data into the event object in one handler and read it in another one, so you can pass to handlers on parents information about the processing below.
Happy coding!! ๐
Posted on December 10, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 18, 2024