preface

The React source reference version is 17.0.3. This is React source code series seven, suggest the first look at the source of the students from the first start to see, so more coherent, there are source series links below.

The warm-up to prepare

Define several concepts

In the [email protected] version:

  • All events are delegate inid = rootThe DOM element (many online say indocument,17Version is not);
  • All nodes in the application are actually listening for eventsid = rootDOM element;
  • ReactA set of event bubble capture mechanism is implemented.
  • ReactComposite events are implementedSyntheticEvent;
  • Reactin17Version no longer uses event pools. (There is a lot of talk online about using object pools to manage the creation and destruction of synthetic event objects16Version and before);
  • Once the eventid = rootIn the DOM element, the delegate is actually fired all the time, but there is no bound callback function;

The theft of an official map, according to the official interpretation, will delegate the event fromdocumentThe move toid = rootThe DOM element of theNesting old and new React trees is more secure.

If you’re interested, visit the React Chinese website.

Event System role division

  • Event registration:registerEvents;
  • Event listening:listenToAllSupportedEvents;
  • Event synthesis:SyntheticBaseEvent;
  • Event distribution:dispatchEvent;

Event registration

Event registration is self-executing, that is, invoked by React itself:

// Register React events registerSimpleEvents(); registerEvents$2(); registerEvents$1(); registerEvents$3(); registerEvents();Copy the code

React events are events written as onClick that are called in a component. The above is divided into five functions, mainly to distinguish different event registration logic, but finally will be added to the Set data structure of allNativeEvents.

registerSimpleEvents

This is where you register most of the events, which are defined as top-level events in React.

They fall into three categories:

  • Discrete events:discreteEvent, such as:click, keyup, change;
  • User blocking event:userBlocking, such as:dragEnter, mouseMove, scroll;
  • Continuous events:continuous, such as:error, progress, load, ;

Their priorities are sorted:

0: discrete event, 1: user blocking event, 2: continuous event

They register two events, the bubble and capture phases.

registerEvents$2

Register single-phase events like onMouseEnter, onMouseLeave, and only register bubbling phase events.

registerEvents$1

Register onChange related events, register both the bubble and capture phases.

registerEvents$3

Register for onSelect related events, register for both the bubble and capture phases.

registerEvents

Register onBeforeInput, onCompositionUpdate and other related events, and register the two events of the bubble and capture phases.

Event listeners

As mentioned in React source series 2: React Rendering Mechanism, React creates a fiberRoot node for the application before it starts rendering. One more thing we do when we build fiberRoot is

listenToAllSupportedEvents(rootContainerElement);
Copy the code

The rootContainerElement argument is the DOM element in the application with id = root.

This function mainly traverses the above events to register the events added to allNativeEvents, according to certain rules, distinguish the bubble phase, capture phase, distinguish whether there are side effects to listen, listening API or addEventListener:

Function addEventBubbleListener(target, eventType, listener) {target.addeventListener (eventType, listener, listener); false); return listener; } function addEventCaptureListener(target, eventType, listener) {target.addeventListener (eventType, listener); listener, true); return listener; }Copy the code

The target in the code is the DOM element with id = root.

Note that the listener above is an event dispatcher, not the actual browser event or the event callback function you wrote. Don’t get confused.

Provided by the event

As mentioned above, once the event is delegated in the DOM element with id = root, it actually fires all the time, but there is no callback bound to it.

This means that when we move the mouse over the page of our application, we are sending events because the DOM element of the page is listening for events like Mousemove.

How does React know that we have a callback function bound to the event and that it fires the corresponding callback function?

With that in mind, let’s look at event distribution.

The listener for the event listener phase is actually the following:

function createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags) {
  var eventPriority = getEventPriorityForPluginSystem(domEventName);
  var listenerWrapper;

  switch (eventPriority) {
    case DiscreteEvent:
      listenerWrapper = dispatchDiscreteEvent;
      break;

    case UserBlockingEvent:
      listenerWrapper = dispatchUserBlockingUpdate;
      break;

    case ContinuousEvent:
    default:
      listenerWrapper = dispatchEvent;
      break;
  }

  return listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer);
}
Copy the code

And event registration, the listener is divided into dispatchDiscreteEvent, dispatchUserBlockingUpdate, dispatchEvent 3 kinds. The main difference between them is the execution priority, and there are discreteEvents that involve clearing up previous discreteevents, so there are no discreteEvents for them. But they all end up calling dispatchEvent.

So the role of event dispatcher should be dispatchEvent

function dispatchEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) { var allowReplay = true; allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0; // If a discrete event is executing, it is queued, Order the if (allowReplay && hasQueuedDiscreteEvents () && isReplayableDiscreteEvent (domEventName)) {domEventName, eventSystemFlags, targetContainer, nativeEvent); return; } // Try event dispatch, if successful, AttemptToDispatchEvent (domEventName, eventSystemFlags, targetContainer, nativeEvent) ¶ If (blockedOn === null) {if (allowReplay) {// clearIfContinuousEvent(domEventName, nativeEvent); } return; } if (allowReplay) { if (isReplayableDiscreteEvent(domEventName)) { queueDiscreteEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent); return; } if (queueIfContinuousEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent)) { return; } clearIfContinuousEvent(domEventName, nativeEvent); } dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, null, targetContainer); }Copy the code

The parameters of dispatchEvent are described below:

  • domEventName: DOM event name, for example:click, notonClick;
  • eventSystemFlags: Event system marker;
  • targetContainer:id=rootThe DOM element;
  • nativeEvent: Native events (fromaddEventListener);

In attemptToDispatchEvent, the DOM element that really triggered the event was found according to nativeEvent. Target, and the corresponding fiber node was found according to the DOM element. The type of fiber node and whether it has been rendered were determined to dispatch the event.

After a series of judgments are passed, the actual event processing begins:

function dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, TargetContainer) {var nativeEventTarget = getEventTarget(nativeEvent); Var dispatchQueue = []; ExtractEvents $5(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags); ProcessDispatchQueue (dispatchQueue, eventSystemFlags); }Copy the code

Event synthesis takes place in extractEvents$5 and is covered separately below.

The processDispatchQueue determines whether to walk through the listeners of the composite events in either a positive or reverse order based on the event phase (bubble or capture).

And then it gets easier. Traversal Listeners execute the listener above.

Synthetic events

In a composite event, the domEventName determines which type of composite event to use.

Take click as an example. When we click on an element on the page, React will find the DOM element that triggered the event and the corresponding fiber node based on the nativeEvent nativeEvent. Then go up to that node as the child node and create a dispatchListener for that node and all of the click callbacks above and add it to the Listeners array.

// dispatchListener {instance: instance, // fiber-node listener: listener, // event callback function currentTarget: CurrentTarget // The DOM element corresponding to the event}Copy the code

When the upward lookup is complete, the event is created based on the composite event class of type Click

Var _event = new SyntheticEventCtor(reactName, reactEventType, null, nativeEvent, nativeEventTarget); Dispatchqueue. push({event: _event, // Compose event instances: _listeners // Collect arrays of events});Copy the code

Look at the SyntheticEventCtor

Function createSyntheticEvent(Interface) {function SyntheticBaseEvent(reactName, reactEventType, targetInst, nativeEvent, nativeEventTarget) { this._reactName = reactName; this._targetInst = targetInst; this.type = reactEventType; this.nativeEvent = nativeEvent; this.target = nativeEventTarget; this.currentTarget = null; For (var _propName in Interface) {... } var defaultPrevented = nativeEvent.defaultPrevented ! = null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false; if (defaultPrevented) { this.isDefaultPrevented = functionThatReturnsTrue; } else { this.isDefaultPrevented = functionThatReturnsFalse; } this.isPropagationStopped = functionThatReturnsFalse; return this; } _assign (SyntheticBaseEvent prototype, {/ / prevents the default event the preventDefault: function () {... StopPropagation: function () {... }, // The synthesized event does not use the object pool. This event is empty and has no meaning. It is saved for backward compatibility. persist: function () {}, isPersistent: functionThatReturnsTrue }); return SyntheticBaseEvent; }Copy the code

Now, we can pretty much figure out what a composite event is.

React preventDefault and stopPropagation should be overwritten. React preventDefault should be overwritten and preventDefault should be overwritten.

In this way, events of the same type can reuse the same composite event instance object, saving the cost of creating an event instance object for each event separately, which is event synthesis.

Capture and bubble

Event dispatch is performed in two phases, the capture phase and the bubble phase.

As mentioned in event synthesis above, React will look up the fiber node triggered by the event and add events of the same type to the queue. Thus, there is a natural bubbling sequence, bubbling up from the bottom. If you go backwards that’s the order of capture.

React is a simple process to implement, but it’s a simple process to use to determine whether an event is sent in a positive or reverse direction.

conclusion

I’m talking about React composite events, but I’m actually talking about React’s event system. To sum up:

React’s event system is divided into several parts:

  • Event registration;
  • Event listening;
  • Event synthesis;
  • Event distribution;

Event system flow:

  1. When React code executes, events are automatically registered internally;

  2. For the first render, when fiberRoot is created, events will be listened for. All events will be listened on the DOM element with ID =root via addEventListener delegate.

  3. When we trigger an event, we synthesize the event, reuse a synthesized event class instance object of the same type;

  4. Finally, the event is distributed and the event callback function in our code is executed.

Of course, due to the space problem, here is also a concise analysis of the React event system, some places may be ignored, welcome to correct.

After reading this article, we can understand the following questions:

  1. ReactWhere is the event delegate?
  2. ReactWhat is a composite event?
  3. ReactHow are composite events implemented?
  4. ReactHow is bubbling and capturing implemented?
  5. ReactAre synthetic events native events used?
  6. ReactWhat are the parts of the event system?

Article series arrangement:

  1. React Fiber;
  2. React source series 2: React rendering mechanism;
  3. React source series 3: hooks useState, useReducer;
  4. React code series 4: hooks useEffect;
  5. React code Series 5: Hooks useCallback, useMemo;
  6. React source Series 6: Hooks useContext;
  7. React source series 7: React synthesis events;
  8. React source series eight: React diff algorithm;
  9. React source series 9: React update mechanism;
  10. React source series 10: Concurrent Mode;

Reference:

React official documents;

Making;