React event synthesis refers to the synthesis of native events into a React event. The reason why we encapsulate our own set of event mechanisms is to achieve consistency across browsers and smooth out differences between browsers. For example, the native onclick event corresponds to the onClick composite event in React. Let’s first look at how React events are used versus native events:

const handleClick = (e) = >{e.preventDefault(); }// Native events
<div onclick="handleClick()"></div>

// React to a composite event
<div onClick={HandleCilck}></div>
Copy the code

As you can see, React synthetic events are named in a hump, while native events are named in all lower case. In addition, React event handlers take the form of event objects, and native events take the form of strings.

Flow of events

We already know that the onClick event is a composite event. How does a composite event relate to a native event? First, let’s review the event flow principle:

Image from Event Flow

As shown in the figure above, the so-called event flow consists of three phases: event capture, target phase, and event bubble. < span style = “box-width: border-box; color: RGB (74, 74, 74); -> target, the target phase is the phase where the event actually occurs and is processed, the event bubbles from the inside out, corresponding to the target in the diagram ->… -> HTML -> document -> window. React synthetic events work in roughly two phases:

  1. event
  2. Events trigger

Before React17, React delegated events on document. React17 and later versions no longer delegate events on document, but on the mounted container. This article takes React version 16.x as an example to explore React synthesis events. When the actual DOM triggers an event, the React synthesized event object is constructed, and the actual event handler is collected according to the bubble or captured path. In this process, the native event is processed first, and then the React event is processed after the bubble has reached the Document object. Here’s an example:

import React from 'react';
import './App.less';

class Test extends React.Component {
  parentRef: React.RefObject<any>;

  childRef: React.RefObject<any>;

  constructor(props) {
    super(props);
    this.parentRef = React.createRef();
    this.childRef = React.createRef();
  }

  componentDidMount() {
    document.addEventListener(
      'click'.() = > {
        console.log(Document Native event capture);
      },
      true,);document.addEventListener('click'.() = > {
      console.log('Document native event bubble');
    });
    this.parentRef.current.addEventListener(
      'click'.() = > {
        console.log('Parent element native event capture');
      },
      true,);this.parentRef.current.addEventListener('click'.() = > {
      console.log('Parent element native event bubble');
    });
    this.childRef.current.addEventListener(
      'click'.() = > {
        console.log(Child element native event capture);
      },
      true,);this.childRef.current.addEventListener('click'.() = > {
      console.log(Child element native event bubble);
    });
  }

  handleParentBubble = () = > {
    console.log('Parent element React event bubble');
  };

  handleChildBubble = () = > {
    console.log('Child element React event bubble');
  };

  handleParentCapture = () = > {
    console.log('Parent element React event capture');
  };

  handleChileCapture = () = > {
    console.log(Child element React event capture);
  };

  render() {
    return (
      <div
        ref={this.parentRef}
        onClick={this.handleParentBubble}
        onClickCapture={this.handleParentCapture}
      >
        <div
          ref={this.childRef}
          onClick={this.handleChildBubble}
          onClickCapture={this.handleChileCapture}
        >Event handling test</div>
      </div>); }}export default Test;

Copy the code

The printed result of the above case is:

Note: The execution of the above case in React17 will be different, all capture events will be executed before all bubble events.

event

In this case, we know the React synthesis event and the native event execution process, the two are actually linked by a module called EventPlugin, each plug-in only handles the corresponding synthesis event, such as onClick event corresponding to SimpleEventPlugin plug-in, So the React at the beginning will bring these plug-ins loading in, through the plug-in initialization some global objects, such as one of the objects is registrationNameDependencies, it defines the synthesis with native events corresponding relationship is as follows:

{
    onClick: ['click'],
    onClickCapture: ['click'],
    onChange: ['blur'.'change'.'click'.'focus'.'input'.'keydown'.'keyup'.'selectionchange'],... }Copy the code

The registrationNameModule object specifies the mapping of React events to the corresponding plug-in:

{
    onClick: SimpleEventPlugin,
    onClickCapture: SimpleEventPlugin,
    onChange: ChangeEventPlugin,
    ...
}
Copy the code

The Plugins object is a list of these plug-ins. In the rendering process of a node, the composite event such as onClick is used as its prop. If the prop is judged to be of event type, the corresponding dependent native event is found according to the composite event type and registered and bound to the top-level document. DispatchEvent is a unified event handler function.

Events trigger

The dispatchEvent function is executed when any event is triggered. For example, in the above example, when the user clicks the Child div, all the parent elements of the element will be traversed, and the event will be collected for each element in turn. Construct a SyntheticEvent object (i.e., the default event of a custom function in React, where the native event object corresponds to one of its properties) and form a “chain” that in turn stores the syntheticEvents in an eventQueue. It then traverses the eventQueue to simulate the capture and bubble phases, and then triggers the invocation of each listener in turn via the runEventsInBatch method. During this process, it determines whether the event type is bubbling or capturing. For example, onClick is bubbling. OnClickCapture is triggered during the capture phase and released after event processing is complete. The SyntheticEvent object properties are as follows:

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent	// Native event objects
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
void persist()
DOMEventTarget target
number timeStamp
string type
Copy the code

The pseudo-code of dispatchEvent is as follows:

dispatchEvent = (event) = > {
    const path = []; // Synthesize the chain of events
    let current = event.target; // Triggers the event source
    while (current) {
      path.push(current);
      current = current.parentNode; // Collect the data step by step
    }
    // Simulate the capture and bubble stages
    // path = [target, div, body, html, ...]
    for (let i = path.length - 1; i >= 0; i--) {
      const targetHandler = path[i].onClickCapture;
      targetHandler && targetHandler();
    }
    for (let i = 0; i < path.length; i++) {
      consttargetHandler = path[i].onClick; targetHandler && targetHandler(); }};Copy the code

conclusion

Because event objects can be created and reclaimed frequently in React16.x, SyntheticEvent uses event pools. SyntheticEvent is managed in pools to reduce memory overhead. React is designed to be compatible with different browsers by synthesizing events, simulating the capture and bubble phases. In addition, React does not recommend using native and composite events together, which can cause confusion.

The last

Search the official Eval Studio account for more updates