Event Capturing and Bubbling in React

eladtzemach

Elad Tzemach

Posted on December 10, 2020

Event Capturing and Bubbling in React

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>
  );
Enter fullscreen mode Exit fullscreen mode

That renders this button:

Screen Shot 2020-12-10 at 10.27.49 AM

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:

Screen Shot 2020-12-10 at 10.30.19 AM

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:

  1. Capturing Phase - the event starts from the window down until it reaches the event.target.
  2. Target Phase - the event has reached the event.target. The most deeply nested element that caused the event is called a target element, accessible as event.target.
  3. Bubbling Phase - the event bubbles up from the event.target element up until it reaches the window, 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.

hjayqa99iejfhbsujlqd

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>
Enter fullscreen mode Exit fullscreen mode

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!! ๐Ÿš€

๐Ÿ’– ๐Ÿ’ช ๐Ÿ™… ๐Ÿšฉ
eladtzemach
Elad Tzemach

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

ยฉ TheLazy.dev

About