Under the hood of event listeners in React

Recently, during the migration to React 17, I had a problem between event listeners handled by React and one added on document manually. It was due to the part Changes to Event Delegation of the React 17 release note.

At this moment I understood that…


This content originally appeared on DEV Community and was authored by Romain Trotard

Recently, during the migration to React 17, I had a problem between event listeners handled by React and one added on document manually. It was due to the part Changes to Event Delegation of the React 17 release note.

At this moment I understood that I had a misconception of how React handles event listener. So I decided to explore the React code to understand how it works.

Spoiler alert: I will simplify a lot the process made by React to make the article easier to read and that you don't quit it before the end :D.

The misconception

Before going deep in the React codebase, I would like to explain what was in my head about the management of event listeners.

For example when I write this simple code:

function App() {
  return (
     <button onClick={() => console.log('Click on the button')}>
        Click me
     </button>
  );
}

In my head, React was doing under the hood, something like:

// `buttonRef` an imaginary reference added by React on the button
buttonRef.addEventListener('click', onClick);

How it really works

After reading the React 17 release note. I was like "What? React was attaching event handlers on document and now on rootNode".

Ps: All this article will be based on the version v17.0.2 of React.

Event handlers creation

Handled events

React initializes at runtime multiple objects to know how to handle event listeners. For example there are:

  • An array of all native events that are handled:
const handledNativeEvents = ['click', 'change', 'dblclick', ...]
  • An object that does a mapping between the native events and the event handlers properties:
const reactEventHandlerPropByEventName = {
   'click': 'onClick',
   'dblclick': 'onDoubleClick',
   ...
}

*Note: * There are also a notion of priorities, but we will not talk about it in this article:

const prioritiesByEventName = {
  'click': 0, // DiscreteEvent
  'drag': 1,  // UserBlockingEvent
  'load': 2,  // ContinuousEvent
  ...
};

Root/Container node Fiber creation

Actually the event handlers registrations, are made during the creation of the root Fiber node.

Let's look at the entry point in your application, where React is initialized:

import { StrictMode } from "react";
import ReactDOM from "react-dom";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <StrictMode>
    <App />
  </StrictMode>,
  rootElement
);

The code behind the ReactDOM.render handles at the same time the creation an the update of the Fiber tree.

How does React know if it needs to create the Fiber tree?
Actually React store in the root DOM node, a key named _reactRootContainer. You can get it in your browser by typing:

// In my example, my container id is `root`
document.getElementById('root')._reactRootContainer

Get FiberRootNode from browser

SO if there is a value the Fiber tree has been already created otherwise let's create it.

Finally during the creation, all the events that are handled by React are looped to create event listeners linked to the root DOM node:

handledNativeEvents.forEach(eventName => {
      rootDomNode.addEventListener(eventName, myEventListener);
      rootDomNode.addEventListener(eventName, myEventListener, true);
  }

What are the added event listeners?
By going a little deeper in the code, we can see that the listeners call a same method named dispatchEvent (with different priorities) that will handle the event.

This is the method that we will talk about in the next part.

And now we can see in the browser that React added the listener to the root DOM node in developer console:
Event listener added by react on root node

Trigger of this event handlers

Now that we know how and where React add event listeners. The question we can ask to ourself is: "How the callback I put on the onClick property of my button is called".

Some magic

We need to know some magic that React does on DOM nodes.

Actually React puts on DOM nodes a reference to the Fiber node under a dynamic key named [internalInstanceKey] and the props under the key [internalPropsKey].

*Note: * If you want to know values of internalInstanceKey and internalPropsKey (and that you don't have a smart console that helps you with autocomplete) you will have to add a debugger point on react-dom code to get it.

How to go into the react-dom code?
You will need to install the React Developer Tools and then follow this little gif:
Open react-dom code in dev console

Then after refresh, we finally can get the wanted values:
Get keys values for instance and props

Warning: If you refresh again the page, it will not be the same values!

Process after click

With the following example, what does it happen when clicking on the button:

function App() {
  return (
     <button onClick={() => console.log('Click on the button')}>
        Click me
     </button>
  );
}

We have seen previously that the listener added by React will call the method dispatchEvent.

From the event, we can have the target DOM node and thanks to the key internalInstanceKey we can have the Fiber node instance of this DOM node, in our case the button.

Note: The target will be the clicked element and the currentTarget the root DOM node element (on which the event listener is applied).

From the clicked Fiber node, we can go up in the Fiber tree until the root node.
For each Fiber node, React watch if the component is a HostComponent (ie a htnml element) and if there is a prop corresponding to React event handler thanks to the object reactEventHandlerPropByEventName, in our case we search onClick prop. This listeners are stored in an array named dispatchQueue.

Note: Basically it traverses the Fiber tree node, from the target node to the root node element.

Here is a little gif to understand the process that gets listeners and fills the dispatchQueue:
Gif of the process to get listeners

Then this dispatchQueue will be processed by executing these listeners in order:

function executeDispatchQueue(event) {
  for (const listener of dispatchQueue) {
    listener(syntheticBaseEvent);
  }
}

Event sent by React

If you put a debugger point, in the onClick method of the button. You can see that the type of the event is not a MouseEvent but a SyntheticBaseEvent

Indeed React wraps the native event, into a React event:

const syntheticBaseEvent = {
  nativeEvent,
  target,
  currentTarget,
  type,
  _reactName,
  ...
}

Why are nativeEvent wrapped?
It helps reducing cross-browser inconsistencies.

Conclusion

So to conclude React does not add event handlers on each DOM element on which we put a event handler like onClick, but only the root node have all event listeners that React knows handle the event.

Then when the user trigger the event, the event listener is called. From the target of the event, React can get the Fiber node because it puts reference to Fiber nodes in DOM nodes into a dynamic key.
From that Fiber node, React goes up in the tree, to get all listener that match the React event name and put them in an array (a dispatch queue). Then all the callback from that dispatch queue are executed.

If you want more information about it with links to React code, let's go here.

Want to see more ? Follow me on Twitter or go to my Website. ?


This content originally appeared on DEV Community and was authored by Romain Trotard


Print Share Comment Cite Upload Translate Updates
APA

Romain Trotard | Sciencx (2021-06-12T19:46:58+00:00) Under the hood of event listeners in React. Retrieved from https://www.scien.cx/2021/06/12/under-the-hood-of-event-listeners-in-react/

MLA
" » Under the hood of event listeners in React." Romain Trotard | Sciencx - Saturday June 12, 2021, https://www.scien.cx/2021/06/12/under-the-hood-of-event-listeners-in-react/
HARVARD
Romain Trotard | Sciencx Saturday June 12, 2021 » Under the hood of event listeners in React., viewed ,<https://www.scien.cx/2021/06/12/under-the-hood-of-event-listeners-in-react/>
VANCOUVER
Romain Trotard | Sciencx - » Under the hood of event listeners in React. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/06/12/under-the-hood-of-event-listeners-in-react/
CHICAGO
" » Under the hood of event listeners in React." Romain Trotard | Sciencx - Accessed . https://www.scien.cx/2021/06/12/under-the-hood-of-event-listeners-in-react/
IEEE
" » Under the hood of event listeners in React." Romain Trotard | Sciencx [Online]. Available: https://www.scien.cx/2021/06/12/under-the-hood-of-event-listeners-in-react/. [Accessed: ]
rf:citation
» Under the hood of event listeners in React | Romain Trotard | Sciencx | https://www.scien.cx/2021/06/12/under-the-hood-of-event-listeners-in-react/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.