React source code 18 Event system

Video courses (efficient Learning) :Into the curriculum

Course Contents:

1. Introduction and questions

2. React design philosophy

React source code architecture

4. Source directory structure and debugging

5. JSX & core API

Legacy and Concurrent mode entry functions

7. Fiber architecture

8. Render phase

9. The diff algorithm

10. com MIT stage

11. Life cycle

12. Status update process

13. Hooks the source code

14. Handwritten hooks

15.scheduler&Lane

16. Concurrent mode

17.context

18 Event System

19. Write the React mini

20. Summary & Answers to interview questions in Chapter 1

21.demo

Let’s start with a bug

Is there any difference between the following demo_13 in REact17 and React16? And the code is pretty simple, you simulate a Modal box, you click show up, you click somewhere else, you click mask, modal disappears, because react events are delegated to the upper layer, so you need to prevent bubbles in handleClick so that when you click show it doesn’t trigger event call-back on the document, Therefore, modal cannot be displayed. But found on react16 do it or not, you need to call e.n ativeEvent. StopImmediatePropagation () can be achieved, and little influence on react17

The reason for this is that react16 and 17 have made a change in the container for the delegate event. The react16 event will bubble on the document and the 17 event will bubble on the root container, the second parameter of reactdom.render

export default class Demo13 extends React.Component {
  state = { show: false };
  componentDidMount() {
    document.addEventListener("click".() = > {
      this.setState({ show: false });
    });
  }
  handleClick = (e) = > {
    e.stopPropagation();/ / react17 effect
    // e.nativeEvent.stopImmediatePropagation(); // The stopImmediatePropagation effect in REact16 also prevents this level of listener function from executing
    this.setState({ show: true });
  };
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>According to</button>
        {this.state.show && <div onClick={(e)= > e.nativeEvent.stopImmediatePropagation()}>modal</div>}
      </div>); }}Copy the code

You can also see how demo_11 and demo_12 differ in the firing order of REact16 and 17. The event.html in the demo project also simulates the event proxy mechanism of REact16 and 17

Event system architecture diagram

Let’s take a look at the event registration, binding, and firing process using SimpleEvent as an example, and watch the debugging process in the video

Event registration

  1. Domplugineventsystem.js registers events by calling the registerEvents method of the SimpleEventPlugin plugin

    //DOMPluginEventSystem.js
    SimpleEventPlugin.registerEvents();
    Copy the code
  2. registerSimpleEvents

    function registerSimpleEvents() {
      registerSimplePluginEventsAndSetTheirPriorities(discreteEventPairsForSimpleEventPlugin, DiscreteEvent);
      / /...
    }
    
    function registerSimplePluginEventsAndSetTheirPriorities(eventTypes, priority) {
      for (var i = 0; i < eventTypes.length; i += 2) {
        var topEvent = eventTypes[i];
        var event = eventTypes[i + 1];
        var capitalizedEvent = event[0].toUpperCase() + event.slice(1);
        var reactName = 'on' + capitalizedEvent;
        eventPriorities.set(topEvent, priority);
        topLevelEventsToReactNames.set(topEvent, reactName);
        registerTwoPhaseEvent(reactName, [topEvent]);// Register capture and bubble events}}Copy the code
  3. registerTwoPhaseEvent

    function registerTwoPhaseEvent(registrationName, dependencies) {
      registerDirectEvent(registrationName, dependencies);
      registerDirectEvent(registrationName + 'Capture', dependencies);
    }
    Copy the code
  4. registerDirectEvent

    function registerDirectEvent(registrationName, dependencies) {
     / /...
    
      for (var i = 0; i < dependencies.length; i++) {
        allNativeEvents.add(dependencies[i]);// Generate the allNativeEvents object}}Copy the code

event

  1. listenToAllSupportedEvents

    // called by createRootImpl, that is, after the root node is created
    function listenToAllSupportedEvents(rootContainerElement) {
        allNativeEvents.forEach(function (domEventName) {
          if(! nonDelegatedEvents.has(domEventName)) { listenToNativeEvent(domEventName,false, rootContainerElement, null);
          }
    
          listenToNativeEvent(domEventName, true, rootContainerElement, null); }); }}Copy the code
  2. listenToNativeEvent

    function listenToNativeEvent(domEventName, isCapturePhaseListener, rootContainerElement, targetElement) {
     / /...
    
      if(! listenerSet.has(listenerSetKey)) {if(isCapturePhaseListener) { eventSystemFlags |= IS_CAPTURE_PHASE; } addTrappedEventListener(target, domEventName, eventSystemFlags, isCapturePhaseListener); listenerSet.add(listenerSetKey); }}Copy the code
  3. addTrappedEventListener

    function addTrappedEventListener(targetContainer, domEventName, eventSystemFlags, isCapturePhaseListener, isDeferredListenerForLegacyFBSupport) {
      // Create a listener with a priority
      var listener = createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags); 
      / /...
      targetContainer =  targetContainer;
      var unsubscribeListener; 
    
      if (isCapturePhaseListener) {// Add events to the node
        if(isPassiveListener ! = =undefined) {
          unsubscribeListener = addEventCaptureListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener);
        } else{ unsubscribeListener = addEventCaptureListener(targetContainer, domEventName, listener); }}else {
        if(isPassiveListener ! = =undefined) {
          unsubscribeListener = addEventBubbleListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener);
        } else{ unsubscribeListener = addEventBubbleListener(targetContainer, domEventName, listener); }}}Copy the code
  4. createEventListenerWrapperWithPriority

    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;
      }
    	/ / bind dispatchDiscreteEvent
      return listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer);
    }
    Copy the code

Events trigger

  1. dispatchDiscreteEvent(dispatchEvent)

    function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
      {
        flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp);
      }
    
      discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent);
    }
    Copy the code
  2. unstable_runWithPriority

    function unstable_runWithPriority(priorityLevel, eventHandler) {EventHandler is the dispatchEvent function
      switch (priorityLevel) {
        case ImmediatePriority:
        case UserBlockingPriority:
        case NormalPriority:
        case LowPriority:
        case IdlePriority:
          break;
    
        default:
          priorityLevel = NormalPriority;
      }
    
      var previousPriorityLevel = currentPriorityLevel;
      currentPriorityLevel = priorityLevel;
    
      try {
        return eventHandler();
      } finally{ currentPriorityLevel = previousPriorityLevel; }}Copy the code
  3. batchedEventUpdates

    function batchedEventUpdates(fn, a, b) {
      if (isBatchingEventUpdates) {
        return fn(a, b);
      }
    
      isBatchingEventUpdates = true;
    
      try {
        return batchedEventUpdatesImpl(fn, a, b);
        // the fn parameter is:
        //function () {
        // return dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, //ancestorInst);
      	/ /}
        function () {
        returndispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, ancestorInst); }}finally {
        isBatchingEventUpdates = false; finishEventHandler(); }}Copy the code
  4. dispatchEventsForPlugins

    function dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) {
      var nativeEventTarget = getEventTarget(nativeEvent);
      var dispatchQueue = [];
      / / extractEvent generates SyntheticEvent
      extractEvents(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags);
      ProcessDispatchQueue Execution forms an event queue
      processDispatchQueue(dispatchQueue, eventSystemFlags);
    }
    Copy the code