skip to Main Content

I have studied native event and synthetic event.
But I don’t know which one runs faster if native event and synthetic event are in same element. And chat gpt and claude tell me different.
So I ask you.

import { useEffect } from "react";
import "./styles.css";

export default function App() {
  useEffect(() => {
    const a = document.querySelector("#a");
    a?.addEventListener("click", () => console.log("dom a"), true);

    return () => {
      a?.removeEventListener("click", () => console.log("dom a"), true);
    };
  }, []);


  return (
    <div id="a" onClickCapture={() => console.log("react a")}>
      <div id="b" onClick={() => console.log("b")}>
        <div id="c" onClick={() => console.log("c")}>
          <div id="e" onClick={() => console.log("e")}></div>
        </div>
        <div id="d" onClick={(e) => console.log(e)}></div>
      </div>
    </div>
  );
}

I try this code. And ‘react a’ prints first.
But I don’t know why.

2

Answers


  1. You are calling the console.log("react a") code in the onClickCapture event, so react a is printed before other events. If you change it to the onClick event, react a will be printed last.

    It seems you attempted to set the handler for console.log("dom a") to capture mode, but there is an error. It should be written as follows:

    a?.addEventListener("click", () => console.log("dom a"), { capture: true });
    

    However, even with this change, the handler for console.log("react a") is registered first, so react a will still be printed first.

    Login or Signup to reply.
  2. A Synthetic event in React is basically a custom event. Creating custom events and Dispatching it are widely known in Web programming. The very same fundamentals of events in Browsers are the only requisite knowledge to understand the synthetic events too.

    We shall discuss it below.

    As we know, in general, the events in Browsers propagate in three phases.

    1. Capture phase
    2. Target phase
    3. Bubble up phase

    Firstly, an event happens in an element placed in a document contained in a Browser Window, will firstly be routed to the Capture phase handlers. We also know, capture phase handlers are placed on to the containers. Since there could be multiple containers for an element, the search for Capture handlers will start and run from the outermost container to the inner most container of the given target object.
    Therefore the below code will capture each and every click event in a web app since window is the outer most container of it.

    window.addEventListener('click', () => console.log('ClickCapture handler in the window object'), true);
    

    Secondly, the originated event will be routed to the target object, the object from where the event originated. We may call the codes over here as target phase handlers. There could be multiple handlers for the same event attached to the target object. The order of executing it will depend on the order of attaching the handlers.

    Thirdly, the event will start bubbling up to its containers. Bubble up of events is from the innermost to the outermost container which is essentially in the opposite direction of running Capture phase handlers.

    This much of Events fundamentals would suffice for us to start discussing about the Synthetic events in React. We shall now move on it.

    First case. Synthetic and Native events – Bubble up phase events only

    Let us take first bubble up phase events only with the following sample code.

    App.js

    import { useEffect, useState } from 'react';
    
    console.clear();
    export default function App() {
      useEffect(() => {
        document.querySelector('#div').addEventListener('click', (e) => {
          console.log('Native click - div');
        });
        document.querySelector('#button').addEventListener('click', (e) => {
          console.log('Native click - Button');
        });
      }, []);
    
      return (
        <>
          <div id="div" onClick={(e) => console.log('React onClick - Div')}>
            <button
              id="button"
              onClick={(e) => console.log('React onClick - Button')}
            >
              Button
            </button>
          </div>
        </>
      );
    }
    

    Output

    On clicking the button, the following log generated

    // Native click - button
    // Native click - div
    // React onClick - button
    // React onClick - div
    

    Observation

    a. As there is no Capture phase events in the document, the event handler search will find the target phase handlers first.

    b. There are two handlers here in button element. The first one is added by React and the second one is added as a native event in useEffect. We can see that the native event handler has run here first.

    c. Then the search for bubble up phase handlers will start. There are two handlers in the container element div here. Similar to button, it is added by React and useEffect respectively. And we could see in the output as the native click of the container has been executed next.

    d. Subsequent to the two log entries we have discussed above, the remaining two log entries are generated by the React event handlers.

    e. Over here, the log entry ‘React onClick – button’ is the result of the React handler in target phase, and the next entry ‘React onClick – div’ is the result of the React handler bubble up phase.

    The two of most outstanding questions here may be the below.

    Why did the native click event on the button run first before the React event on it ?

    We shall find out the reasons below.

    While we check the event listeners on the button element as below, we could see there is only one handler over there, and that too, the native event handler added in useEffect. And there is no handler for the React event onClick over here.

    enter image description here

    if so, from where did the console log ‘React onClick – button’ has been generated ?

    The answer to the question is, it is the result of a custom event dispatched by some container. Let us see, how did it happen.

    As we saw, after the target phase event – the native click handler, the event moved on to bubble up phase. As the result of it, we could see the output ‘Native click – div’. As we mentioned earlier, the bubble up phase will continue to the outermost container. We also know we have not coded in the outermost object – the window, to respond to a click event. So there is some container in between the div and the window object to handle this bubbled up click event.

    We shall now inspect the below element and see it has anything in this regard. Please see the below screen. In the hierarch of html elements in the document, we could see the root div element has a handler for click event. And the handler attached to it is named as
    dispatchDiscreteEvent. This is the common handler provided by React. It has the logic in it to create a custom event of same kind and dispatch it to the target element.

    Therefore this handler did its job. It created a custom event under the name OnClick and dispatched it to the button element, and therefore it generated the respective log entry. Now it is not all over. Just like the browser generated events, this OnClick event will bubble up after its target phase is over. Since this event has bubbled up, the onClick handler on the container div has also been executed which resulted the log ‘React onClick – div’. It bubbled up again, and ended since there is no further handler in the hierarchy.

    This is the reasoning behind the four log entries in the particular order.

    enter image description here

    Second case. Synthetic and Native events – Capture phase events

    Let us take capture phase events only with the following sample code.

    App.js

    import { useEffect, useState } from 'react';
    
    console.clear();
    export default function App() {
      useEffect(() => {
        document.querySelector('#div').addEventListener(
          'click',
          (e) => {
            console.log('Native clickCapture - div');
          },
          { capture: true }
        );
        document.querySelector('#button').addEventListener('click', (e) => {
          console.log('Native click - button');
        });
      }, []);
    
      return (
        <>
          <div
            id="div"
            onClickCapture={(e) => console.log('React onClickCapture - div')}
            onClick={(e) => console.log('React onClick - div')}
          >
            <button
              id="button"
              onClick={(e) => console.log('React onClick - button')}
            >
              Button
            </button>
          </div>
        </>
      );
    }
    

    Test run

    On clicking the button, the following log entries generated.

    // React onClickCapture - div
    // Native clickCapture - div
    // Native click - button
    // React onClick - button
    // React onClick - div
    

    Observation

    We know this code is different from the first one. The only major difference is it has Capture phase events. Therefore we shall go straight to discuss the working details.

    It generated ‘React onClickCapture – div’ as the first log entry, since it ran the Capture handler first. However the following question is outstanding here ?

    Why or How did it run the React handler instead of the native handler, though both are capture phase events ?.
    Moreover, in the first case, it ran the native handler first in its target phase.

    The reason may be known to us by now. We know in general, the Capture phase events if present, will run first, from the outer to the innermost container.

    Now the next question may be, why did it run React capture phase event first ?

    It runs it so since this time the event handler search starts from the top, and it goes to bottom. Therefore, the common handler of React comes again in into the context. Let us see it in the below screenshot.

    enter image description here

    Please note the major difference between this handler and the one in the first case, the property useCapture is true here. In the first case, it was false.

    It means the handler shown below is exclusively here to receive the Capture phase click events from the children, and delegate the same kind of event to the target object. The basic action it does is the same, still the kind of event it generates over here is different.

    Therefore this common handler will generate a Capture phase onClick event, and dispatch it to the next enclosing container which is the div object in this case. As a result, we could see the log ‘React onClickCapture – div’ as its first entry.

    As we know, Capture phase event will descend, but there no more containers or handlers here for the React onClickCapture event.

    To start processing the target phase events handling, there must be no Capture phase event be pending. However, we have one more to process. The one added by the Native event. It will also then be processed from top to bottom. As a result, we got the log entry ‘Native clickCapture – div’ as the second one.

    Now the remaining three log entries may be self explanatory as it is same as that in the first case.

    The Capture phase events are over, target phase will start processing. Therefore we got ‘Native click – button’ as the third entry. The React onClick will not run then since there is no such handler directly attached to the button as we discussed in the first case.

    Therefore the bubble up phase will start then. It will hit the root div again but for onClick event, and from there, onClick event will be dispatched to the target, as a result, ‘React onClick – button’ will generate as the fourth entry. Since there is no more target phase handlers, the same event – onClick, will bubble up. As a result, it will hit the div, and will generate ‘React onClick – div’ as the fifth entry.

    This is the reasoning behind the five log entries in the particular order.

    Additional content

    As we mentioned in the beginning, creating a custom event and dispatching it is well known web programming.

    The following sample code demoes the same.

    App.js

    import { useEffect, useState } from 'react';
    
    console.clear();
    export default function App() {
      useEffect(() => {
        document.querySelector('#root').addEventListener('click', (e) => {
          const event = new Event('clickCustomEvent', { bubbles: true });
          console.log('step 1. the div element receives and delegates the click event to the target button as clickCustomEvent.');
          e.target.dispatchEvent(event);
        });
        document.querySelector('#div').addEventListener('clickCustomEvent', (e) => {
          console.log(
            'step 3 : the immediate container div also receives the same clickCustomEvent as it is bubbled up.'
          );
        });
        document
          .querySelector('#button')
          .addEventListener('clickCustomEvent', (e) => {
            console.log('step 2: the button element receives the delegated event click CustomEvent');
          });
    
      }, []);
    
      return (
        <>
          <div id="root">
            <div id="div">
              <button id="button">Button</button>
            </div>
          </div>
        </>
      );
    }
    

    Test run

    On clicking the button, the following log entries generated.

    /*
    
    step 1. the div element receives and delegates the click event to the target button as clickCustomEvent.
    
    step 2: the button element receives the delegated event click CustomEvent
    
    step 3 : the immediate container div also receives the same clickCustomEvent as it is bubbled up.
    
    */
    

    Observation

    By now, it may be self explanatory. You may please post your comments if any.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search