When we set up the event handler on the component, React does not bind the event handler directly to the DOM element. React provides a customized event system for subscribes to and distributes events in a unified manner.

Specifically, React uses the event delegate mechanism to listen for DOM events on the Document and distribute events to specific component instances based on the triggered target. E is a SyntheticEvent, not the original DOM event.


The article Outlines

  • So why customize an event system?
  • The basic concept
    • Overall architecture
    • Event classification and priority
  • Implementation details
    • How are events bound?
    • How are events distributed?
      • Event triggered scheduling
      • How do plug-ins handle events?
      • Batch execution
  • In the future
    • The creation of Responder
    • What’s the point of react-events?
  • Further reading

As of this writing, the React version is 16.8.6


So why customize an event system?

If you’ve read Preact(I wrote a previous article parsing Preact’s source code), Preact cuts a lot of React stuff, including the event mechanism. Preact binds events directly to DOM elements.

Before studying a subject, I first ask why? Understanding its motivation will help you to have an essential understanding of it.

React has several motivations for customizing an event system:

  • 1. Smooth out compatibility differences between browsers. This is probably the original motivation. React defines these synthetic events according to the W3C specification to smooth out differences between browsers.

    React will also try to simulate events that are incompatible with lower versions of React via other related events. .

  • 2. Event ‘composition’, that is, event customization. In addition to dealing with compatibility issues, event composition can also use custom advanced events. A typical example is the React onChange event, which defines uniform value change events for form elements. It is also possible for third parties to compose custom events using React’s event plugin mechanism, although few people do this.

  • 3. Abstract the cross-platform event mechanism. Similar to VirtualDOM, which abstracts cross-platform rendering, SyntheticEvent is intended to provide an abstract cross-platform event mechanism.

  • 4. React plans to do more optimizations. With event delegation, for example, most events end up bound to a Document rather than the DOM node itself. This simplifies DOM event processing logic and reduces memory overhead. But it also means React needs to simulate its own set of event bubbling mechanisms.

  • 5. React intends to interfere with the distribution of events. In V16, Fiber architecture was introduced. React interfered with event distribution to optimize user interaction experience. Different types of events have different priorities, such as high-priority events that interrupt rendering and allow user code to respond to user interactions in a timely manner.


Ok, we will look into the React event implementation later. I will try not to post code and use flow diagrams.


The basic concept

Overall architecture

  • ReactEventListener – Event handler. This is where the event handler is bound. When the DOM fires an event, the React component tree is dispatched from here

  • ReactEventEmitter – Exposes interfaces to the React component layer for adding event subscriptions

  • EventPluginHub – As the name suggests, this is a ‘plug-in slot’ that manages and registers various plug-ins. At event distribution, the plug-in is called to generate the composite event

  • The plugin-React event system uses a Plugin mechanism to manage events with different behaviors. These plug-ins handle the event types they are interested in and generate synthetic event objects. ReactDOM currently has the following plug-in types:

    • SimpleEventPlugin – A simple event that handles some of the more general event types, such as Click, Input, keyDown, mouseOver, mouseOut, pointerOver, pointerOut

    • EnterLeaveEventPlugin mouseEnter/mouseLeave and pointerEnter/pointerLeave is more special, the two types of events over the / * and * out events than they do not support the event bubbling, * Enter sends events to all incoming elements, which behaves somewhat like :hover; *over, on the other hand, bubbles to notify its superiors when it enters an element. You can use this example to see the difference between Enter and over.

      If the tree is deep, a large number of MouseEnter fires can cause performance problems. In addition, it does not support bubbling, which makes it impossible to listen and distribute perfectly in Document, so ReactDOM uses *over/*out events to simulate these *enter/*leave events.

    • The ChangeEventplugin-change event is a custom event for React that normalizes changes to form elements.

      It supports these form elements: input, Textarea, SELECT

    • SelectEventPlugin – Like the change event, React normalizes the Select (selection range change) event for form elements, which applies to input, Textarea, and contentEditable elements.

    • BeforeInputEventPlugin – beforeInput event and composition event handling.

    This article focuses on the implementation of the SimpleEventPlugin, but you can read the React source code for yourself.

  • EventPropagators traverse the React component tree in two stages of DOM event propagation and collect event handlers for all components.

  • EventBatching is responsible for batch execution of event queues and event handlers to handle event bubbling.

  • SyntheticEvent this is the base class for ‘synthesized’ events that can correspond to DOM Event objects. React uses a pool of objects to build and release event objects in order to reduce memory consumption and garbage collection. This means that syntheticEvents cannot be used for asynchronous references. Syntheticevents are released after synchronous execution of the event handler.

    SyntheticEvent also has subclasses that match DOM specific event types:

    • SyntheticAnimationEvent
    • SyntheticClipboardEvent
    • SyntheticCompositionEvent
    • SyntheticDragEvent
    • SyntheticFocusEvent
    • SyntheticInputEvent
    • SyntheticKeyboardEvent
    • SyntheticMouseEvent
    • SyntheticPointerEvent
    • SyntheticTouchEvent
    • .


Event classification and priority

The SimpleEventPlugin divides event types into three categories, with different priorities (from lowest to highest):

  • DiscreteEvent indicates a DiscreteEvent. For example, blur, focus, click, submit, touchStart. These events are triggered discreetly
  • UserBlockingEvent UserBlockingEvent. Examples include touchMove, mouseMove, Scroll, drag, dragOver, etc. These events’ block ‘user interaction.
  • ContinuousEvent Continuable events. Examples are load, error, loadStart, abort, animationEnd. This has the highest priority, which means that they should be executed immediately and synchronously. That’s what Continuous means.

You may need to understand the priorities of React schedules to understand the differences between these three event types. As of this writing, React has five priority levels:

  • Immediate– Tasks of this priority are executed synchronously, or immediately without interruption
  • UserBlocking(250ms timeout) These tasks are generally the result of user interaction and require immediate feedback.
  • Normal(5s timeout) Respond to tasks that do not need to be felt immediately, such as network requests
  • LowThese tasks can be deferred, but should be executed eventually. For example, analysis notifications
  • Idle(no timeout) some unnecessary tasks (e.g., hidden content).

ContinuousEvent corresponds to the Immediate priority. UserBlockingEvent corresponds to UserBlocking(manually enabled); DiscreteEvent also corresponds to UserBlocking, but it executes other Discrete tasks before executing them.

This article doesn’t go into the details of the React Fiber architecture, but interested readers can read the extended reading list at the end of the article.





Implementation details

How does React implement the event mechanism? There are two main parts: binding and distribution.

How are events bound?

To avoid confusion, it’s important to understand the React event mechanism’s plug-in protocol. The structure of each plug-in is as follows:

export type EventTypes = {[key: string]: DispatchConfig};

// Plug-in interface
export type PluginModule<NativeEvent> = {
  eventTypes: EventTypes,          // Declare the event types supported by the plug-in
  extractEvents: (                 // Process the event and return the synthesized event object
    topLevelType: TopLevelType,
    targetInst: null| Fiber, nativeEvent: NativeEvent, nativeEventTarget: EventTarget, ) => ? ReactSyntheticEvent, tapMoveThreshold? :number};Copy the code


EventTypes declares the event type for which the plug-in is responsible, which is described by DispatchConfig:

export type DispatchConfig = {
  dependencies: Array<TopLevelType>, "Simple events" usually have only one, while complex events such as onChange listen for more than one, as shown in the following figure 👇phasedRegistrationNames? : {React will use these names to look up the corresponding props event handler in the component instance
    bubbled: string.// Bubble phase, such as onClick
    captured: string.// Capture phase, such as onClickCapture}, registrationName? :string      // Props event registration names, such as onMouseEnter, which do not support bubbling. only registrationName is defined, not phasedRegistrationNames
  eventPriority: EventPriority,  // The priority of the event, as described above
};
Copy the code


Take a look at an example:

Three typical EventPlugins are listed above:

  • SimpleEventPlugin – Simple events are best understood because they behave in a general way and don’t have any tricks, such as no support for event bubbling, no support for binding on documents, etc. And native DOM events are one-to-one correspondence, easier to handle.

  • EnterLeaveEventPlugin – As you can see from the figure above, mouseEnter and mouseLeave rely on mouseOut and mouseover events. React uses the *Over/*Out event to simulate the *Enter/*Leave event. This has the advantage of being able to delegate listening on top of the document, as well as avoiding some weird and impractical behavior like *Enter/*Leave.

  • Changeeventplugin-onchange is a React custom event that relies on a variety of native DOM event types to simulate onChange events.


Each plug-in also defines the extractEvents method, which takes the event name, the native DOM event object, the DOM element triggered by the event, and the React component instance, and returns a synthesized event object. If it returns nothing, it will not be processed. Details about extractEvents are covered in the next section.


These plug-ins are registered with The EventPluginHub when ReactDOM starts:

EventPluginHubInjection.injectEventPluginsByName({
  SimpleEventPlugin: SimpleEventPlugin,
  EnterLeaveEventPlugin: EnterLeaveEventPlugin,
  ChangeEventPlugin: ChangeEventPlugin,
  SelectEventPlugin: SelectEventPlugin,
  BeforeInputEventPlugin: BeforeInputEventPlugin,
});
Copy the code


Ok, back to the subject, how are events bound? Make a breakpoint to see the call stack:

How the React tree is updated and rendered is beyond the scope of this article. As shown in the React stack, event binding is performed when the React props are initialized and updated. Here’s a look at the flow chart, ignoring the messy jumps:

  • 1. Event binding is performed during the props initialization and update. First React determines whether the element isThe media type.Media-type events cannot be listened for in Document, so they are bound directly to the element
  • 2. Otherwise bind on DocumentThis requires two pieces of information. One is the ‘event dependency list’ mentioned above, for exampleonMouseEnterRely onmouseover/mouseout; The second is the ‘subscribed event list’ maintained by ReactBrowserEventEmitter.The event handler only needs to subscribe to the Document once, so it saves a lot of resources compared to subscribing to events on every element.

The code looks like this:

export function listenTo(
  registrationName: string.// Register a name, such as onClick
  mountAt: Document | Element | Node, // Component tree container, usually Document
) :void {
  const listeningSet = getListeningSetForElement(mountAt);             // Subscribed event list
  const dependencies = registrationNameDependencies[registrationName]; // Event dependency

  for (let i = 0; i < dependencies.length; i++) {
    const dependency = dependencies[i];
    if(! listeningSet.has(dependency)) {/ / not subscribe
      switch (dependency) {
        / /... Special event listener handling
        default:
          constisMediaEvent = mediaEventTypes.indexOf(dependency) ! = =- 1;
          if(! isMediaEvent) { trapBubbledEvent(dependency, mountAt);// Set the event handler
          }
          break;
      }
      listeningSet.add(dependency);                                    // Update the subscribed table}}}Copy the code
  • The next step is to set the event handler based on the ‘priority’ and ‘capture stage’ of the event:
function trapEventForPluginEventSystem(
  element: Document | Element | Node,   // Bind to the element, usually Document
  topLevelType: DOMTopLevelEventType,   // Event name
  capture: boolean.) :void {
  let listener;
  switch (getEventPriority(topLevelType)) {
    // Different priority event types are distributed by different event handlers, as described below
    case DiscreteEvent:                      // ⚛️ discrete event
      listener = dispatchDiscreteEvent.bind(
        null,
        topLevelType,
        PLUGIN_EVENT_SYSTEM,
      );
      break;
    case UserBlockingEvent:                 // ⚛️ User blocking event
      listener = dispatchUserBlockingUpdate.bind(
        null,
        topLevelType,
        PLUGIN_EVENT_SYSTEM,
      );
      break;
    case ContinuousEvent:                   // ⚛️ can be continuous events
    default:
      listener = dispatchEvent.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM);
      break;
  }

  const rawEventName = getRawEventName(topLevelType);
  if (capture) {                            // Bind the event handler to the element
    addEventCaptureListener(element, rawEventName, listener);
  } else{ addEventBubbleListener(element, rawEventName, listener); }}Copy the code

The process of event binding is relatively simple, so let’s look at how events are distributed.


How are events distributed?

As usual, let’s start with the flow chart:

Event triggered scheduling

Through the above trapEventForPluginEventSystem function can know, different types of events have different event handlers, scheduling priority is the difference between them is not the same as:

// Discrete events
// discrentUpdates are executed in UserBlocking priority
function dispatchDiscreteEvent(topLevelType, eventSystemFlags, nativeEvent) {
  flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
  discreteUpdates(dispatchEvent, topLevelType, eventSystemFlags, nativeEvent);
}

// Block events
function dispatchUserBlockingUpdate(topLevelType, eventSystemFlags, nativeEvent,) {
  / / if the opened enableUserBlockingEvents in UserBlocking priority scheduling,
  // Enable enableUserBlockingEvents to prevent hunger, because blocking events include scroll, mouseMove, and other frequently triggered events
  // Otherwise, the execution will be synchronized
  if (enableUserBlockingEvents) {
    runWithPriority(
      UserBlockingPriority,
      dispatchEvent.bind(null, topLevelType, eventSystemFlags, nativeEvent),
    );
  } else{ dispatchEvent(topLevelType, eventSystemFlags, nativeEvent); }}// dispatchEvent is called synchronously for continuous events
Copy the code


Finally, the dispatchEvent function is called for different event types. The dispatchEvent function obtains the target triggered by the event from the DOM native event object, and then obtains the associated React node instance based on this target.

export function dispatchEvent(topLevelType: DOMTopLevelEventType, eventSystemFlags: EventSystemFlags, nativeEvent: AnyNativeEvent) :void {
  // Get the target DOM of the event
  const nativeEventTarget = getEventTarget(nativeEvent);
  // Get the component instance closest to the DOM (only DOM element components)
  let targetInst = getClosestInstanceFromNode(nativeEventTarget);
  / /...
  dispatchEventForPluginEventSystem(topLevelType, eventSystemFlags, nativeEvent, targetInst);
}
Copy the code


Here, then, there are some steps to ignore calls EventPluginHub runExtractedPluginEventsInBatch, this method through the plugin list to handle events, generate a SyntheticEvent list:

export function runExtractedPluginEventsInBatch(
  topLevelType: TopLevelType,
  targetInst: null | Fiber,
  nativeEvent: AnyNativeEvent,
  nativeEventTarget: EventTarget,
) {
  // Iterate over the list of plug-ins, calling the extractEvents of the plug-in to generate the list of SyntheticEvents
  const events = extractPluginEvents(
    topLevelType,
    targetInst,
    nativeEvent,
    nativeEventTarget,
  );

  // Event handler execution, see batch Execution below
  runEventsInBatch(events);
}
Copy the code


How do plug-ins handle events?

Now to see how the plug-in handles events, let’s take SimpleEventPlugin as an example:

const SimpleEventPlugin: PluginModule<MouseEvent> & {
  getEventPriority: (topLevelType: TopLevelType) = > EventPriority,
} = {
  eventTypes: eventTypes,
  // Extract the event object
  extractEvents: function(topLevelType: TopLevelType, targetInst: null | Fiber, nativeEvent: MouseEvent, nativeEventTarget: EventTarget,) :null | ReactSyntheticEvent {
    // Event configuration
    const dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];

    // 1️ one gets the SyntheticEvent subclass event constructor based on the event type
    let EventConstructor;
    switch (topLevelType) {
      // ...
      case DOMTopLevelEventTypes.TOP_KEY_DOWN:
      case DOMTopLevelEventTypes.TOP_KEY_UP:
        EventConstructor = SyntheticKeyboardEvent;
        break;
      case DOMTopLevelEventTypes.TOP_BLUR:
      case DOMTopLevelEventTypes.TOP_FOCUS:
        EventConstructor = SyntheticFocusEvent;
        break;
      / /... omit
      case DOMTopLevelEventTypes.TOP_GOT_POINTER_CAPTURE:
      // ...
      case DOMTopLevelEventTypes.TOP_POINTER_UP:
        EventConstructor = SyntheticPointerEvent;
        break;
      default:
        EventConstructor = SyntheticEvent;
        break;
    }

    // 2️ one event object, fetched from the object pool
    const event = EventConstructor.getPooled(
      dispatchConfig,
      targetInst,
      nativeEvent,
      nativeEventTarget,
    );

    // 3️ one obtains user event handlers according to the order of DOM event propagation
    accumulateTwoPhaseDispatches(event);
    returnevent; }};Copy the code

The extractEvents of SimpleEventPlugin do three things:

  • 1️ Determine SyntheticEvent constructor according to the type of event
  • 2️ construction of SyntheticEvent object.
  • 3️ obtain the list of user event handlers according to the order of DOM event propagation


React uses an event pool to manage event objects in order to avoid performance degradation (object creation and garbage collection) caused by frequent creation and release of event objects.

This also means that the SyntheticEvent object will be reclaimed immediately after the event handler completes its synchronization, invalidating all of its properties. So SyntheticEvent event objects are generally not accessed in asynchronous operations. You can also keep a reference to the event object by:

  • callSyntheticEvent#persist()Method that tells React not to recycle to the object pool
  • Direct referenceSyntheticEvent#nativeEvent, nativeEvent can be persistent, but in order not to break abstraction, it is recommended not to reference nativeEvent directly


Once the SyntheticEvent object is built, we need to traverse the component tree to get the user event handler that subscribed to the event:

function accumulateTwoPhaseDispatchesSingle(event) {
  // Traverses the component tree in the order in which DOM events are propagated, starting with _targetInst
  traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
}
Copy the code

The traversal method is actually quite simple:

export function traverseTwoPhase(inst, fn, arg) {
  const path = [];
  while (inst) {           // Start with inST and work your way up
    path.push(inst);
    inst = getParent(inst);
  }

  let i;
  // The capture phase starts from the topmost parent component and propagates to the children
  for (i = path.length; i-- > 0;) { fn(path[i],'captured', arg);
  }

  // The bubble phase, which starts at the INST, the event trigger point, propagates upward
  for (i = 0; i < path.length; i++) {
    fn(path[i], 'bubbled', arg); }}Copy the code

AccumulateDirectionalDispatches function is easy to find whether the current node has a corresponding event handler:

function accumulateDirectionalDispatches(inst, phase, event) {
  // Check whether an event handler exists
  const listener = listenerAtPhase(inst, event, phase);
  // All processors are placed in the _dispatchListeners queue, which are then executed in batches
  if(listener) { event._dispatchListeners = accumulateInto( event._dispatchListeners, listener, ); event._dispatchInstances = accumulateInto(event._dispatchInstances, inst); }}Copy the code


For example, in the following component tree, the traversal would look like this:

The final _dispatchListeners queue looks like this: [handleB, handleC, handleA]


Batch execution

After iterating through the execution plug-in, you get a list of SyntheticEvents, runEventsInBatch being the _dispatchListeners event queue for batch execution of these events

export function runEventsInBatch(
  events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null,
) {
  // ...
  forEachAccumulated(processingEventQueue, executeDispatchesAndRelease);
}

/ / 👇

const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
  if (event) {
    // Execute _dispatchListeners in order
    / / 👇
    executeDispatchesInOrder(event);

    If the persist() method is not called, it is recycled
    if(! event.isPersistent()) { event.constructor.release(event); }}};export function executeDispatchesInOrder(event) {
  / / traverse dispatchListeners
  for (let i = 0; i < dispatchListeners.length; i++) {
    // The next event handler can be disabled by calling the stopPropagation method
    if (event.isPropagationStopped()) {
      break;
    }
    // Execute the event handlerexecuteDispatch(event, dispatchListeners[i], dispatchInstances[i]); }}Copy the code

The React event mechanism is basically introduced here. This is just a brief introduction to the SimpleEventPlugin. There are a lot of details about event handling in the actual code. Interested readers can check out the React source code for themselves.





In the future

React has an experimental event API, known internally as React Flare and formally as React – Events, that enables advanced event encapsulation across platforms and devices.

React – Events defines the concept of an Event responder ** that captures events from a subcomponent tree or application root node and converts them into custom events.

Typical advanced events are the gestures press, longPress and swipe. Often we need to implement this set of gesture recognition ourselves or using third-party libraries, for example

import Gesture from 'rc-gesture';

ReactDOM.render(
  <Gesture
    onTap={handleTap}
    onSwipe={onSwipe}
    onPinch={handlePinch}
  >
    <div>container</div>
  </Gesture>,
container);
Copy the code


The purpose of React-Events is to provide a universal event mechanism for developers to encapsulate ‘advanced events’, even across platforms and devices. Now you can use React-Events to encapsulate gesture events.

In addition to the core Responder interface, react-Events encapsulates several built-in modules to implement advanced event encapsulation across platforms:

  • Focus module
  • Hover module
  • Press module
  • FocusScope module
  • Input module
  • KeyBoard module
  • Drag module
  • Pan module
  • Scroll module
  • Swipe module

For example, the Press module responds to Press events for the elements it wraps. Press events include onContextMenu, onLongPress, onPress, onPressEnd, onPressMove, and onPressStart. The underlying layer is converted by mouse, pen, touch, Trackpad and other events.

Take a look at an example use:

import { PressResponder, usePressListener } from 'react-events/press';

const Button = (props) = > (
  const listener = usePressListener({  // ⚛️ create Responder with hooks
    onPressStart,
    onPress,
    onPressEnd,
  })

  return (
    <div listeners={listener}>
      {subtrees}
    </div>); ;Copy the code


Event Responders are mounted to the host node. They listen to DOM or React Native events distributed by the host node or its children and convert/combine them into advanced events:


You can play react-events with this Codesanbox:


The creation of Responder

Let’s pick a simple module to learn some core apis of React – Events. The simplest module is Keyboard module. The purpose of the Keyboard module is to normalize the key property of keyDown and KeyUp event objects (some key properties of browsers have different behaviors). Its implementation is as follows:

/** * implements Responder */
const keyboardResponderImpl = {
  /** * 1️ defines the DOM event of the subtree that Responder needs to listen on, which for Keyboard is ['keydown', 'keyup';] * /
  targetEventTypes,
  /** * 2️ listener on subtree-triggered events */
  onEvent(
    event: ReactDOMResponderEvent,     // Contains information about the current event, such as the native event object, the node that the event triggered, the event type, and so on
    context: ReactDOMResponderContext, // The Responder context gives the Responder some method to drive event distribution
    props: KeyboardResponderProps,     // Pass props to Responder) :void {
    const {responderTarget, type} = event;

    if (props.disabled) {
      return;
    }

    if (type === 'keydown') {
      dispatchKeyboardEvent(
        'onKeyDown',
        event,
        context,
        'keydown',
        ((responderTarget: any): Element | Document),
      );
    } else if (type === 'keyup') {
      dispatchKeyboardEvent(
        'onKeyUp',
        event,
        context,
        'keyup', ((responderTarget: any): Element | Document), ); }}};Copy the code

Let’s look at dispatchKeyboardEvent:

function dispatchKeyboardEvent(eventPropName: string, event: ReactDOMResponderEvent, context: ReactDOMResponderContext, type: KeyboardEventType, target: Element | Document,) :void {
  // ⚛️ creates a composite event object in which the key property of the event is normalized
  const syntheticEvent = createKeyboardEvent(event, context, type, target);
  // ⚛️ distributes events through the Responder context
  context.dispatchEvent(eventPropName, syntheticEvent, DiscreteEvent);
}
Copy the code

Export the Responder:

// ⚛️createResponder converts the keyboardResponderImpl to component form
export const KeyboardResponder = React.unstable_createResponder(
  'Keyboard',
  keyboardResponderImpl,
);

// ⚛️ create the hooks form
export function useKeyboardListener(props: KeyboardListenerProps) :void {
  React.unstable_useListener(KeyboardResponder, props);
}
Copy the code


Responder now has a basic idea of what Responder does. It does the following things:

  • Declare native events (such as DOM) to listen on, as shown abovetargetEventTypes
  • Process and transform synthetic events, as shown aboveonEvent
  • Create and distribute custom events. ascontext.dispatchEvent


Compared to the Keyboard module above, many advanced events in the real world, such as longPress, are much more complex to implement. They may maintain state or may have exclusive ownership of the response (i.e., only one Responder can handle the event at a time. This is commonly used for mobile touch gestures such as the React Native GestureResponderSystem).

React-events currently considers these scenarios. Take a look at the API overview:


Details can be found in the official react-Events repository


What’s the point of react-events?

As mentioned above, React events use plug-in mechanism to realize event processing and synthesis. A typical example is onChange events. The onChange event is a so-called “high-level event” that is simulated through various native events of the form component.

That said, React essentially encapsulates high-level events through the plugin mechanism. But if you look at the source code, you’ll find the logic convoluted and rely on many of React’s internal implementations. So this internal plug-in mechanism is not intended for the average developer.

The React-Events interface is much simpler, masking a lot of internal details for the average developer. It can be used for high-performance custom event distribution and, more importantly, for cross-platform/device event handling.

At present, React-Events is still in the experimental stage, and the feature is turned off by default. API may change, so it is not recommended to use it in production environment. Follow its progress through this Issue.


Finally, praise the React team for their innovation!


Finished!


Further reading

  • Research on the problem of Chinese triggering multiple input events
  • Fully understand React Fiber
  • Lin Clark — A Cartoon Intro to Fiber — React Conf 2017
  • Scheduling in React
  • [Umbrella] React Flare
  • react-events