An overview of

The so-called event mechanism consists of two steps: registering events and distributing events. The programmer registers events to the nodes corresponding to React through code. The user interacts with the browser, triggering the browser’s native event, which is captured by React and gets the native node from the native event. The react event mechanism is how to register the react event and how to distribute the react event.

Take out the event

React initialization is the process of converting our JSX into a string of JSON called a tree of react elements, and then converting the string into a Fiber tree. The process of generating a Fiber tree is basically: We perform a depth-first traversal of the React element tree. During the traversal, the event we bind to each node is actually bound to it as a property. After traversal, we need to generate a real DOM tree. Each real DOM node is associated with a Fiber node (via a key value, internalInstanceKey)

Register event

When we generate the real DOM tree, we need to map some properties of the Fiber tree (such as styles) to the real DOM. When we find that the node is bound to an event, we directly bind the event type to the root node or document.

1 function addEventBubbleListener(target, eventType, listener) { 2 console.log(target, eventType, listener.name); 3 target.addEventListener(eventType, listener, false); 4 return listener; 5}Copy the code

React listens for events on the root node, so the listener is a function that can handle the response events of all nodes. The final event registered in the browser should look like this. For each event type, a listener is registered on the root node for event distribution.

Distribute events

When the user interacts with the browser, the following events are executed in sequence.

Get the native Event

Trigger the native event and get the event of the native event.

Full code address: addEventCaptureListener

1 document.addEventListener(type, DispatchEvent) 2 function dispatchEvent(event) {3 console.log(event)Copy the code

Get the Fiber node

Based on the event of the native event, you get the native DOM node, and then the Fiber node. (the dom nodes are already associated with the internalInstanceKey and fiber nodes when the actual dom tree is generated)

Complete code address: attemptToDispatchEvent

1 const nativeEventTarget = getEventTarget(nativeEvent); 2 const targetInst = getClosestInstanceFromNode(nativeEventTarget); 3 4 function getClosestInstanceFromNode(targetNode: Node) { 5 let targetInst = (targetNode: any)[internalInstanceKey]; 6 if (targetInst) { 7 // Don't return HostRoot or SuspenseComponent here 8 return targetInst; 10 9}}Copy the code

Synthesized React event

React events are synthesized from native events. Before we do this, we will introduce the concept of react synthesized events.

The core concept

Most browser events are directly derived from events, and 7 other UI events are derived from UIEvent, and UIEvent is derived from events. The same is true inside React.

I won’t bore you with the details, but the main thing to do is

  1. The encapsulation of native events basically defines some properties of native events inside the code itself.
  2. Upgrades and reworks of native events (change, SELECT, beforeInput, etc.) that are registered with dependencies such as onchange for input, So “blur”, “change”, “click”, “focus”, “input”, “keyDown “,” keyUp “, “selectionChange” are all registered. React is a native event that needs to be triggered when the focus is out of focus, so react is a great way to compensate.
  3. Compatible handling of events across browsers.
1 const nativeEventTarget = getEventTarget(nativeEvent); 2 const dispatchQueue: DispatchQueue = []; ExtractEvents (dispatchQueue); ProcessDispatchQueue (dispatchQueue, eventSystemFlags);Copy the code

The synthesis process

  1. React combines all events into six plugins based on the event type
  2. According to the type of event, instantiate different React constructor for synthesis of events, code address: SimpleEventPlugin. ExtractEvents
1  let EventConstructor;
2  switch (topLevelType) {
3    case DOMTopLevelEventTypes.TOP_KEY_DOWN:
4    case DOMTopLevelEventTypes.TOP_KEY_UP:
5      EventConstructor = SyntheticKeyboardEvent;
6      break;
7    // other case ...
8  }
9  const event = new EventConstructor(
10    reactName,
11    null,
12    nativeEvent,
13    nativeEventTarget,
14  );
Copy the code

Accumulate all instances and listeners

  1. Find the corresponding event callback function based on the Fiber node
1 function getListener() { 2 const stateNode = inst.stateNode; 3 if (stateNode === null) { 4 // Work in progress (ex: onload events in incremental mode). 5 return null; 6 } 7 const props = getFiberCurrentPropsFromNode(stateNode); 8 if (props === null) { 9 // Work in progress. 10 return null; 11 } 12 const listener = props[registrationName]; 13 return listener; 14}Copy the code
  1. Queue events according to capture or bubble stagelistenersAdd a callback function
    • Start by placing callbacks to captured events on listeners
    • If it is an event that needs to be triggered in both the bubble and capture phases, place it at the head of the listener (unshift)
    • If not, the capture phase event is placed at the end of the listener (POP)
1 const listeners: Array<DispatchListener> = []; 3 const bubbled = event._reactName; 4 const captured = bubbled ! == null ? bubbled + 'Capture' : null; CapturePhaseEvents is an enumeration value defined in the code, such as focus, blur 7 const shouldEmulateTwoPhase = capturePhaseEvents.has( 8 ((targetType: any): DOMTopLevelEventType), 9 ); 10 11 if (bubbled ! Const bubbleListener = getListener(instance, bubbled); 14 const entry = createDispatchListener(instance, bubbleListener, currentTarget); 17 Listeners // are aware of the importance of the process. // Other events are added to the following 22 listeners. Push (entry); 23} 24} 25}Copy the code


1  let instance = targetFiber;
2  while (instance !== null) {
3    // do step 1
4    // do step 2
5    instance = instance.return;
6  }
Copy the code

Triggered the callback

Now the listeners are all in the process, looped through, distributed.

1 if (listeners.length ! == 0) { 2 dispatchQueue.push(createDispatchEntry(event, listeners)); 3 } 4 5 function processDispatchQueue( 6 dispatchQueue: DispatchQueue, 7 eventSystemFlags: EventSystemFlags, 8 ): void { 9 const inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) ! = = 0; 10 for (let i = 0; i < dispatchQueue.length; i++) { 11 const {event, listeners} = dispatchQueue[i]; 12 / / combine listners cycle again in 13 processDispatchQueueItemsInOrder execution (event listeners, inCapturePhase); 14 // Modern event system doesn't use pooling. 15 } 16 }Copy the code

Another point is that when one of the callbacks executes stopPropagation, the subsequent code will not execute.

1
Copy the code

Author: Xiang Huijin