This is the ninth day of my participation in the August More text Challenge. For details, see: August More Text Challenge
preface
Explore the underlying principles of React synthesizing events through a simple business scenario.
scenario
The Antd Table is nested with onRow and Popconfirm (bound to the body).
- Select a fixed column in the Table and click to Popconfirm
- Table Click each row to go to other pages
Note: React version 16.13.0
The problem
Click on thePopconfirmThere was also a jump anywhere in the confirmation box.
herePopconfirmIs binding onbodyIn, there is no sumtablePut them together. In the page render view,Table tr tdThere is no nested render inPopconfirm, but clickPopconfirmtriggeredClick the Table onRowEvents.The test here shows that the click on the target element is triggered first, followed by the onRow click.
render: (text) => ( <div> <Popconfirm //.... getPopupContainer={() => document.body} > <div onClick={() => {}}> <Tooltip title={text} theme="dark"> <span>{text}</span> </Tooltip> </div> </Popconfirm> , </div> ) onRow={(record) => { return { onClick: (event) => { window.open("www.baidu.com"); }}; }}Copy the code
The solution
The solution is simple. Popconfirm is on the outer wrapping element, preventing bubbling directly.
onClick={(e) => e.stopPropagation()}
Copy the code
But not only is it fantasizing, but two dom’s, which are basically unrelated, are triggered by a connection, and it’s tempting to wonder what’s going on behind the scenes. Let’s put aside the question and get to the bottom of it. The React event system itself, it’s not a native event system. Instead, composite events are used. Let’s review the React synthesis event.
React composite event
Synthesized events are React custom event objects that comply with the W3C specification, smoothing out the differences between different browsers at the bottom level and exposing developers to a unified, stable event interface that is the same as DOM native events at the top level. Instead of focusing on cumbersome compatibility issues, developers can focus on developing business logic. The benefits of React using synthetic events are as follows:
- React binds events to the Document, preventing many events from being tied to the native DOM.
- It smooths out the differences between browsers at the bottom and exposes developers to a uniform, stable event interface that is identical to DOM native events at the top.
React synthesizes events from event delegation. React’s event system follows the idea of event delegation. In React, except for a few special non-bubbly events (such as media type events) that cannot be processed by the event system, most events are not bound to specific elements, but are bound to the document of the page. When an event is fired on a specific DOM node, it will eventually bubble up to the Document, and the unified event handler bound to the document will distribute the event to the specific component instance.
React first wraps events before distributing them, wrapping native DOM events into composite events.
packaging
Wrap native DOM events into synthetic events.
Popconfirm, button,There is no event listener bound to the Dom. But is it,Noop refers to an empty function.
However, the document binds events that should belong to the target element. As mentioned above, in React (before version 17, version 16 was not bound to document), the events we wrote in our code ended up being bound to document.
Event triggers a click event. What happens to the underlying system?
After a brief understanding of React’s synthetic event mechanism, let’s step back and see what happens when we click on any element in Popconfirm. Let’s break the Document “dispatchDiscreteEvent” function in the source code to see what happens.
Event trigger handler dispatchEvent
When the “dispatchEvent” function is activated, the first important function “dispatchEvent”, “React” event is registered, the “dispatchEvent” is the same as the “dispatchEvent” function, so when we click the button, The dispatchEvent function is executed first.
Native DOM element, find the corresponding fiber
AttemptToDispatchEvent is performed next, and several important things are done in this function
- Find the real dom element based on the nativeEvent object nativeEvent.
- Based on the DOM element, we get the corresponding fiber object, which is the fiber object that we click on
- The event processing system in Legacy mode is displayed
How do I get the fiber object of the DOM element
GetClosestInstanceFromNode in obtaining fiber object, through the function, find the current incoming dom corresponding element type of the object of fiber recently. When React initializes the real DOM, it uses a random key internalInstanceKey pointer to point to the fiber object that corresponds to the current DOM. The fiber object points to the current DOM element with the stateNode. Dom and Fiber objects and they’re related to each other.
Element nodes are associated layer by layer
AttemptToDispatchEvent function executing getNearestMountedFiber found that the tag=5 element nodes are associated layer upon layer from the target node. Even though the Popconfirm element is clicked (attached to the body), the element surrounding it is still associated when it bubbles.
Plug-in Event System scheduling event
Down, then called dispatchEventForLegacyPluginEventSystem, dispatchEventForLegacyPluginEventSystem function literal comprehension is plugin event system scheduling events, In fact, the literal and the essence is similar, is the event system scheduling events. This function starts the event handling system and batch updates in Legacy mode.
DispatchEventForLegacyPluginEventSystem function, first in the React to retrieve the last event pool, assignment of property.
Batch updates are then performed, with batchedEventUpdates (V16) being the primary function for batch updates. The variable isBatchingEventUpdates is used to control whether updates are made in batches.
The main function for event handling is handleTopLevel
HandleTopLevel is the main function for event handling. We write the event handler in code development, and the actual execution is in handleTopLevel(bookKeeping). The handleTopLevel processing logic is to execute the extractEvents handler, such as the onClick event in our Popconfirm element that ends up in the extractEvents function. The reason is that React adopts event synthesis and unified event binding, and the event handler we write in the component is not the real execution function dispatchAciton, so our event object event is also synthesized and processed separately by React. It contains separate methods such as stopPropagation and preventDefault. The benefit is that we don’t need to deal with cross-browser compatibility issues individually, but let the React base handle them uniformly.
Function handleTopLevel(bookKeeping) {//... for (var i = 0; i < bookKeeping.ancestors.length; i++) { // ... runExtractedPluginEventsInBatch(topLevelType, targetInst, nativeEvent, eventTarget, eventSystemFlags); } } function runExtractedPluginEventsInBatch(topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags) { var events = extractPluginEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags); runEventsInBatch(events); } // Find the corresponding event plug-in, and form the corresponding composite event, Function extractPluginEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags) { var events = null; for (var i = 0; i < plugins.length; i++) { var possiblePlugin = plugins[i]; If (possiblePlugin) {/* Find the corresponding event plug-in, form the corresponding composite event, Formation of event execution queue * / var extractedEvents = possiblePlugin. ExtractEvents (topLevelType targetInst, nativeEvent, nativeEventTarget, eventSystemFlags); if (extractedEvents) { events = accumulateInto(events, extractedEvents); } } } return events; } function runEventsInBatch(events) {//... }Copy the code
ExtractEvents – Key, key, key
In the execution of handleTopLevel, we will find the corresponding event plug-in, form the corresponding synthetic event, form the event execution queue, extractEvents is the core function of the whole event system, when we click on the Popconfirm element, The end result is the extractEvents function.
- ExtractEvents will generate the event source object, then start from the event source gradually upward, look for the DOM element type HostComponent corresponding fiber, collect the React synthesis event above, onClick/onClickCapture.
- DispatchListeners collect React composite events from above. For handlers that occur during the event capture phase, the logic is to add the executing function unshift to the top of the queue. In the event bubble phase, the actual event handler, the logic is to push the execution function to the end of the execution queue.
- Finally, the function execution queue is hung on the event object, waiting for execution. From the debugging, it can be seen that the dispatch instance is attached to the _DispatchinpO model and the listening event of the dispatch is attached to the _dispatchListeners, which contains the captured processing event and the bubbling time processing function.
This actually simulates the capture and bubbling of our native events. In short, it’s the same as our native event capture and bubbling. Just to control, I implemented the event system myself. After collecting the simulated event system, it is.
ExtractEvents generates an event source object, SyntheticEvent, which you can see in the following figure.
React forms event queues and event sources before the event is executed, and handles the event default behavior and event bubbling on the event source object. This is the foreshadowing of my previous bug fix.
Event to perform
When everything is ready, the execution of the event begins, which is performed in the function runEventsInBatch.
RunEventsInBatch is a long execution link, so let’s simplify the final and most important execution by locating the function executeDispatchesInOrder, which is designed to dispatch event collections through standard/simple iterations.
function executeDispatchesInOrder(event) { var dispatchListeners = event._dispatchListeners; var dispatchInstances = event._dispatchInstances; { validateEventDispatches(event); } if (Array.isArray(dispatchListeners)) { for (var i = 0; i < dispatchListeners.length; i++) { if (event.isPropagationStopped()) { break; F // 参 考 答 案 executeDispatch(event, dispatchListeners[I], dispatchinlisteners [I]); } } else if (dispatchListeners) { executeDispatch(event, dispatchListeners, dispatchInstances); } event._dispatchListeners = null; event._dispatchInstances = null; }Copy the code
DispatchListeners [I] execute our event handlers, such as the listener handler for the click events we’re developing to write. Here at the time of processing, will determine whether the event. The isPropagationStopped (), is already stop event bubbling. If it’s already organized, it won’t continue to trigger. React prevents event bubbles by using isPropagationStopped. If we call e.propagationPropagation () in the execution queue of the event function, we will assign isPropagationStopped=()=>true, If e.ispropagationStopped () returns true, then the event handler will not execute. To stop e.topPropagation (), Popconfirm is applied to the outer package element. The table onRow click is not triggered.
React17 event mechanism
In React 17, there were three major changes to the event mechanic:
- React will no longer attach event handlers to document. Instead, the event handler is attached to the root DOM container that renders the React tree. In the React 16 or earlier, the React will execute the document for most events. The addEventListener (). 17 will React in the underlying call rootNode. AddEventListener ().
- Native capture event support is finally available in React 17, aligning the browser native standard. At the same time, onScroll events no longer bubble. OnFocus and onBlur use native FocusIn, FocusOut synthesis.
- Cancel the React event pool. 17 Cancel the event pool overcommitment.
conclusion
Finally to sum up, through different breakpoint commissioning, finally found the source of the bug solution. You know what you’re doing. Also indirectly into the React event system. The following diagram is a summary of the event system that the author wrote in the source code.
In React, event firing is essentially a call to the dispatchEvent function. Simulate the capture and bubbling of native events, collect events, and execute them sequentially.
React composite events follow the idea of event delegation, but the implementation process is much more complex than traditional event delegation. For React, the main role of event delegation should be to help React realize the centralized control of all events. So much for the React event system.
If you think it’s good, give it a thumbs up.
reference
- Blog.csdn.net/qianyu62004…
- Kaiwu.lagou.com/course/cour…
- Juejin. Cn/post / 695563…