Examining React's Synthetic Event: the nativeEvent,  the eventPhase, and Bubbling. 
Tags: react, eventdriven, javascript
Published: 2021-06-24 13:13:14

Perhaps you've just started working with React, and you're working with event handlers and you've noticed that when you get the Event Object back, it doesn't look quite the same as it did in vanilla JS.

![Something is different](https://media.giphy.com/media/xT4uQlHibhRkONbLdC/giphy.gif)

What you're getting back instead is the SyntheticEvent (<em>SE</em>) and it contains the original event object in what React has dubbed the nativeEvent (<em>nE</em>). 

#What is the SyntheticEvent?

Straight from the [React Docs](https://reactjs.org/docs/events.html), it is "a cross-browser wrapper around the browser’s native event ... except the events work identically across all browsers."

To examine this, I've built a basic React component with an onClick button. 

``` 
function ButtonDemo(){  

 function showEventDifferences(e) {
      console.log(e)
      console.log(e.nativeEvent)
   }

 return ( 
<div>   
  <button 
  onClick={showEventDifferences}
  className="lookingAtClick">
            Discover Events
    </button>
 </div> )  }
```

This logs the <em>SE</em> first, and the <em>nE</em> second when clicking the discover events button. If you were to click on button within the demo component, you'd get something like this back: 

```
SyntheticBaseEvent 
{_reactName: "onClick", 
_targetInst: null, 
type: "click", 
nativeEvent: MouseEvent, 
target: button.lookingAtClick, …}
altKey: false 
bubbles: true 
button: 0 
buttons: 0 
cancelable: true 
clientX: 259 
clientY: 618 
ctrlKey: false 
currentTarget: null 
defaultPrevented: false
 detail: 1 
eventPhase: 3
 getModifierState: ƒ modifierStateGetter(keyArg) 
isDefaultPrevented: ƒ functionThatReturnsFalse() 
isPropagationStopped: ƒ functionThatReturnsFalse() 
isTrusted: true
 metaKey: false 
movementX: 0 
movementY: 0 
nativeEvent: MouseEvent {isTrusted: true, 
screenX: 1723, screenY: 752, 
clientX: 259, clientY: 618, …}
 pageX: 259
 pageY: 618 
relatedTarget: null
 screenX: 1723 
screenY: 752 
shiftKey: false
 target: button.lookingAtClick 
timeStamp: 734167.6999999881
 type: "click" view: Window {window: Window, self: Window, 
document: document, name: "", location: Location, …} 
_reactName: "onClick" 
_targetInst: null
 __proto__: Object 


MouseEvent {isTrusted: true, 
screenX: 1723, screenY: 752, 
clientX: 259, clientY: 618, …}
altKey: false
bubbles: true 
button: 0
 buttons: 0 
cancelBubble: false 
cancelable: true 
clientX: 259 
clientY: 618 
composed: true 
ctrlKey: false 
currentTarget: null 
defaultPrevented: false 
detail: 1
 eventPhase: 0 
fromElement: null 
isTrusted: true 
layerX: 259 
layerY: 618 
metaKey: false
 movementX: 0 
movementY: 0 
offsetX: 90 
offsetY: 13 
pageX: 259 
pageY: 618 path: (8) [button.lookingAtClick, div,
 div, div#root, body, html, document, Window]
relatedTarget: null 
returnValue: true 
screenX: 1723
 screenY: 752 
shiftKey: false sourceCapabilities: InputDeviceCapabilities 
{firesTouchEvents: false}
 srcElement: button.lookingAtClick target: button.lookingAtClick 
timeStamp: 734167.6999999881 
toElement: button.lookingAtClick
 type: "click" view: Window {window: Window, 
self: Window, document: document, 
name: "", location: Location, …}
 which: 1
 x: 259 
y: 618 
__proto__: MouseEvent

``` 

Let's filter that to make it a little more readable. What the SyntheticEvent provides that's different:

```
SyntheticBaseEvent:
{_reactName: "onClick", 
_targetInst: null, type: "click", 
nativeEvent: MouseEvent, target: button.lookingAtClick, …}
...
eventPhase: 3
 getModifierState: ƒ modifierStateGetter(keyArg) 
isDefaultPrevented: ƒ functionThatReturnsFalse() 
isPropagationStopped: ƒ functionThatReturnsFalse() 
nativeEvent: MouseEvent {isTrusted: 
true, screenX: 1723, screenY: 752, 
clientX: 259, clientY: 618, …}
 _reactName: "onClick" 
_targetInst: null
 __proto__: Object 
```
The mouse event:

```
MouseEvent {isTrusted: true,
 screenX: 1723, screenY: 752, 
clientX: 259, clientY: 618, …}
cancelBubble: false 
composed: true 
eventPhase: 0
currentTarget: null 
layerX: 259 
layerY: 618 
offsetX: 90 
offsetY: 13 
returnValue: true 
sourceCapabilities: InputDeviceCapabilities 
{firesTouchEvents: false}
 srcElement: button.lookingAtClick 
which: 1
 x: 259 
y: 618 
__proto__: MouseEvent
```

And their overlap:

```
altKey: false 
bubbles: true 
button: 0 
buttons: 0 
cancelable: true 
clientX: 259 
clientY: 618 
ctrlKey: false 
defaultPrevented: false
isTrusted: true
metaKey: false
 movementX: 0 
movementY: 0 
pageX: 259
 pageY: 618 
relatedTarget: null
 screenX: 1723 
screenY: 752 
shiftKey: false 
target: button.lookingAtClick 
timeStamp: 734167.6999999881
 type: "click" 
view: Window {window: 
Window, self: Window, document: 
document, name: "", location: Location, …} 
```

When looking at this, what's perhaps surprising is how much this wrapper <em>SE</em> and its <em>nE</em> child have in common. React's <em>SE</em> has bundled at the top level of the <em>SE</em> most of what a dev would need for the majority event handling. This should make the need to drill into the <em>nE</em>  relatively uncommon. Except when you need to do something obvious, such as needing to access the MouseEvent 'which' key value. Enjoy trying to Google what that does. 

![Which which is which](https://media.giphy.com/media/tFG0zsKGeSjou0SgbK/giphy.gif)

##Pulling Back the Curtain

However, some of the differences are also striking. A small one is that you can see a little bit of the abstraction that is taking place under the hood of React with its synthetic wrapper element around the MouseEvent. This was what React is talking about when it discusses how it works across all browsers. Note the __reactName: "onClick", which points to the fact that somewhere in the compilation process (as Javascript is passed through Babel to become JS code that can be read by the browser), there is code that, in rough pseudocode amounts to:

```
 React.createEvent("OnClick", () => {
 if (browser === Safari){
  {return new React.SyntheticObject(Safari)
}
  else if (browser === Chrome){ 
  {return new React.SyntheticObject(Chrome)}
}
  else if ...
```

This way of having React handle the heavy lifting is in stark contrast to, for example, working with vanilla CSS, in which one can spend a fair amount of time and additional space writing repetitive code that ensures that the various browsers will display similar looking experiences by adding -webkit-, -moz-, or various other prefixes to ensure compatibility. 

##Drilling Down to the Root

Beyond giving us a peek at the abstraction, there's something else interesting in this object. Take a look at the __proto__ key. The <em>SE</em> comes with a different class constructor than does the <em>nE</em>! While it is called an Object, this is not the plain old JavaScript object (we'll see that soon enough). Instead, this is where you'll find the .preventDefault(), .stopPropogation() and the now defunct (as of React 17) .persist() method which helped with asynchronous JS due to React previously using a pooling process for its events. When you call any of these methods as part of an event handler function, the reason they work is because they are instanced as part of the <em>SE</em> object itself. 

The relatively small <em>SE</em> prototype though is put to shame by the much more massive <em>nE</em> which has a laundry list of getter functions which allow it to create the various components such as the pagex/y locations of the click, if any buttons are being held at the time of the click, and at what time the event happened (among many others).  It also shows that the MouseEvent object is not the end of the line as far as the nativeElement constructor. The MouseEvent object itself is an extension of the UIEvent class:

```
...
__proto__: MouseEvent
(...)
get x: ƒ x()
get y: ƒ y()
__proto__: UIEvent
  bubbles: (...)
  cancelBubble: (...)
  cancelable: (...)
  composed: (...)
  (...)
```

Which, in turn, is an extension of the Event class ...

```
...
__proto__: UIEvent
(...)
get which: ƒ which()
__proto__: Event
   AT_TARGET: 2
   BUBBLING_PHASE: 3
   CAPTURING_PHASE: 1
   (...)
```

![Jane stop this thing!](https://media.giphy.com/media/4Z5zBIv2DCo8oyAiZX/giphy.gif)

And then finally finds its most basic root class which is a plain old JS object.

```
___proto__: Event
(...)
get timeStamp: ƒ timeStamp()
get type: ƒ type()
__proto__:
   constructor: ƒ Object()
   hasOwnProperty: ƒ hasOwnProperty()
   isPrototypeOf: ƒ isPrototypeOf()
   (...)
```
Told you we'd get here. So why did we trek down this particular rabbit hole? The point is that React's abstraction can be something of a double edged sword. By adding an additional layer of polish which helps us to write code more quickly and cleanly, it can sometimes make it harder to understand what is actually happening. 

##event.eventPhase and Bubbling

This brings me to the final example, the event.eventPhase attribute. For greater detail on the .eventPhase, feel free to parse its [MDN page](https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase), but to keep it short - here it is as follows: 

eventPhase = 0 : No event exists. 
eventPhase = 1 : The event is being capture. To see this phase, instead of calling onClick in React, make use of the onClickCapture, or add 'Capture' to almost all of the 'onAction' events (ie OnChangeCapture). 
eventPhase = 2 : The event has arrived in the code/function and is ready to be used. If there is no *bubbling the eventPhase should terminate here. 
eventPhase = 3 : If there is bubbling, the event terminates after this point. 

Bubbling refers to the fact that when an event is triggered at the local/initial level, it will then proceed to the parent level to look for additional events and if it finds any it will set those events in motion, and then it will continue through connected ancestor elements until all events have been triggered. This 'upward' movement through parent elements in the DOM structure can help you visualize the term as it 'bubbles up'. 

So why does the React onClick element return an eventPhase of 3 when there is nothing else on the page that we have rendered? What is causing the bubbling? If we make a code snippet for vanilla JS that mimics our previous React element, like so:

```
in index.html:

<body>
      <button class="lookingAtClick">Discover Events</button>
   <script src="./index.js"></script>
 </body>

in index.js :

function testEventPhase(e) {
   console.log(e.eventPhase)
   console.log(e)
}

document.querySelector(".lookingAtClick").addEventListener("click", testEventPhase)
```

Why do we get back an eventPhase of 2 on our click? Why would a vanilla JS version eventPhase terminate earlier than the React eventPhase?

 The answer, which we can probably guess from our eventPhase chart is because bubbling is happening. What might not be clear though is that's because <strong>React events always bubble</strong>. This isn't new for a click event, but it is different for some other common JavaScript events such as 'focus', 'blur', and 'change' which do not have this behavior in JS.  This bubbling won't be an issue for our simple console logging button functional component, but not realizing that all events in React trigger other nested events can lead to rapid-onset baldness when trying to debug your code.

![ARGHHH](https://media.giphy.com/media/N7vysOmYh5sru/giphy.gif)

Just remember if this starts happening to you - there is a reason why the aforementioned .stopPropagation() is instanced on the <em>SE</em> class to begin with.

In short, frameworks and libraries can make our lives easier, but they can also make them more confusing if we don't realize that the glossy sheen also has an additional layer of rules, extensions, and interactions on top of our base language. Discovering these nuances and realizing how to troubleshoot the new problems are just part of the process of figuring it all out!