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
-
Domplugineventsystem.js registers events by calling the registerEvents method of the SimpleEventPlugin plugin
//DOMPluginEventSystem.js SimpleEventPlugin.registerEvents(); Copy the 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 capture and bubble events}}Copy the code
-
registerTwoPhaseEvent
function registerTwoPhaseEvent(registrationName, dependencies) { registerDirectEvent(registrationName, dependencies); registerDirectEvent(registrationName + 'Capture', dependencies); } Copy the code
-
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
-
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
-
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
-
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
-
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
-
dispatchDiscreteEvent(dispatchEvent)
function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) { { flushDiscreteUpdatesIfNeeded(nativeEvent.timeStamp); } discreteUpdates(dispatchEvent, domEventName, eventSystemFlags, container, nativeEvent); } Copy the 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 the code
-
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
-
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