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
6. Legacy and concurrent mode entry function
19. Handwritten mini version of react
20. Summary & Answers to Interview Questions in Chapter One
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
-
DOMPluginEventSystem.js will call the registerEvents method of the SimpleEventPlugin plugin to register events
//DOMPluginEventSystem.js SimpleEventPlugin.registerEvents(); Copy code -
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 -
registerTwoPhaseEvent
function registerTwoPhaseEvent ( registrationName, dependencies ) { registerDirectEvent(registrationName, dependencies); registerDirectEvent(registrationName + 'Capture' , dependencies); } Copy code -
registerDirectEvent
function registerDirectEvent ( registrationName, dependencies ) { //... for ( var i = 0 ; i <dependencies.length; i++) { allNativeEvents.add(dependencies[i]); //Generate allNativeEvents object } } Copy code
Event binding
-
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 -
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 -
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 -
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
-
dispatchDiscreteEvent(dispatchEvent)
function dispatchDiscreteEvent ( domEventName, eventSystemFlags, container, nativeEvent ) { { flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp); } discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent); } Copy code -
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 -
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 -
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