React source code analysis 18 event system

React source code analysis 18 event system

React source code analysis 18 event system

Video course (efficient learning): enter the course

Course catalog:

1. Opening introduction and interview questions

2. The design concept of react

3.react source code architecture

4. Source directory structure and debugging

5.jsx&core api

6. Legacy and concurrent mode entry function

7.Fiber architecture

8.render stage

9.diff algorithm

10. Commit stage

11. Life cycle

12. Status update process

13.hooks source code

14. Handwritten hooks

15.scheduler&Lane

16.concurrent mode

17.context

18 event system

19. Handwritten mini version of react

20. Summary & Answers to Interview Questions in Chapter One

21.demo

Speaking from a bug

Is the following demo_13 different between react17 and react16? The code is also very simple. It simulates a modal box, clicks to display it, and clicks elsewhere, which is equivalent to clicking the mask, and the modal disappears. Because the react events are delegated to the upper layer, it is necessary to prevent bubbling at handleClick so that it will not be displayed when clicked. Event callbacks on the document will be triggered, causing the modal to fail to display. But I found that this is still not working on react16, and it needs to be called e.nativeEvent.stopImmediatePropagation() to achieve it, but there is no impact on react17

The reason is that react16 and 17 have made changes in the container of delegated events. The event of react16 will bubble up on the document, while 17 will bubble up on the root container, which is 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(); //Effective in react17 //e.nativeEvent.stopImmediatePropagation();//Effective stopImmediatePropagation in react16 also prevents this level of monitoring functions from executing this .setState({ show : true }); }; render () { return ( < div > < button onClick = {this.handleClick} > Show </button > {this.state.show && < div onClick = {(e) => e.nativeEvent.stopImmediatePropagation()}>modal </div > } </div > ); } } Copy code

You can also see the difference between the trigger sequence of demo_11 and demo_12 in react16 and 17. At the same time, the event.html in the demo project also simulates the event proxy mechanism of react16 and 17.

Event system architecture diagram

Let's take SimpleEvent as an example to see the process of event registration, binding and triggering, and watch the video debugging process

Event registration

  1. DOMPluginEventSystem.js will call the registerEvents method of the SimpleEventPlugin plugin to register events

    //DOMPluginEventSystem.js SimpleEventPlugin.registerEvents(); Copy 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 the event of the two phases of capture and bubbling } } Copy code
  3. registerTwoPhaseEvent

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

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

Event binding

  1. listenToAllSupportedEvents

    //call the function after execution by the createRootImpl, that is, to create a root function listenToAllSupportedEvents ( rootContainerElement ) { allNativeEvents.forEach( function ( domEventName ) { if (!nonDelegatedEvents.has(domEventName)) { listenToNativeEvent(domEventName, false , rootContainerElement, null ); } listenToNativeEvent(domEventName, true , rootContainerElement, null ); }); } } Copy 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 code
  3. addTrappedEventListener

    function addTrappedEventListener ( targetContainer, domEventName, eventSystemFlags, isCapturePhaseListener, isDeferredListenerForLegacyFBSupport ) { //Create a priority listener function var listener = createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags); //... targetContainer = targetContainer; var unsubscribeListener; if (isCapturePhaseListener) { //Add an event 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 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 ; } //Binding dispatchDiscreteEvent return listenerWrapper.bind( null , domEventName, eventSystemFlags, targetContainer); } Copy code

Event trigger

  1. dispatchDiscreteEvent(dispatchEvent)

    function dispatchDiscreteEvent ( domEventName, eventSystemFlags, container, nativeEvent ) { { flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp); } discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent); } Copy 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 code
  3. batchedEventUpdates

    function batchedEventUpdates ( fn, a, b ) { if (isBatchingEventUpdates) { return fn(a, b); } isBatchingEventUpdates = true ; try { return batchedEventUpdatesImpl(fn, a, b); //fn parameters are: //function () { //return dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent,//ancestorInst); //} function () { return dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, ancestorInst); } } finally { isBatchingEventUpdates = false ; finishEventHandler(); } } Copy 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 executes to form an event queue processDispatchQueue(dispatchQueue, eventSystemFlags); } Copy code