On How React Reacts

08 Jun 2017

Web applications often have to respond to user interaction. A few extra-simple applications can do so solely through functions HTML provides, but in most cases it’s not so simple. If you’re a developer you’ve probably used onclick in HTML or .click() in JQuery; these and other event handlers are one of the base elements of user interaction in web applications.

React handles these… a little differently. Here’s my understanding of how.

Web browsers take individual approaches to handling certain events; for example, at some point Opera was able to respond to text events but just never fired any, and the event that’s fired when what text is selected changes is sometimes fired without cause in both Internet Explorer and Chrome. React’s first response to an event it handles being fired is to wrap it up in another object, called a Synthetic Event. This new object has all the properties of the old one; a SyntheticKeyboardEvent will still have the character code and other bits that indicate what’s being pressed (or released).

One of the things React uses consistently for events is ‘ReactBrowserEventEmitter’, which is the only part of React that actually listens to the browser events. RBEE processes native DOM events it’s listening to, since not all browsers fire the same ones; React’s Wheel event corresponds to the native DOM’s wheel, mousewheel, and DOMMouseScroll events, for example. Each event type is only listened for once, even if more than one of your components render an element with that event type. This processing also allows React to support some kinds of events that some browsers don’t, such as mouseenter and mouseleave which Chrome, Safari, AND Firefox don’t support but probably should. They’re much easier to deal with than mouseover and mouseout.

The events are passed through to the EventPluginHub, which asks a number of event “plugins” (no, you can’t add new ones to just your app) to extract the native event into a React synthetic event of an appropriate type. React takes this synthetic event and traverses the document tree to find the target. This simulates the capture and bubble phases of native DOM event propagation. It ignores your composite components! Event handlers can only be attached to DOM elements. The plugin gathers up all the possible dispatches to the target and its ancestors and hands them to EventPropagators, which puts them in the right order. The SyntheticEvent object gets a copy of all of the listeners it’s going to execute at this point!

At this point, React is almost ready to run your listeners. It needs to check whether you’ve called stopPropagation() at some point, since otherwise it’d ignore that. If you did, it just clips off all the dispatches after your call. If not, the whole thing is good. The plugin takes the information back from the Event Propagator and then calls executeDispatchesInOrder(), looping over the collection of handlers the event is going to call. When it calls the handler, it uses another function from React, invokeGuardedCallback() from the error utilities, to make debugging a little bit easier. If you’re in development mode, this function creates a fake node, attaches a native event listener to it, and dispatches a fake native event to that node! This is done so that your debugging is easier. Otherwise, it’s just a try/catch statement with your handler in it. If your handler happened to error, nothing will break until React calls rethrowCaughtError() a little bit later.