React developers are more or less familiar with the concept of synthetic events.
The composite event source code is a lot of code, coupled with a lot of other logic, it is very discouraging to read.
Recently I happened to fix a bug in ANU, and found that anU’s composite event implementation is easy to understand. Why not learn synthetic events through anU?
What is the anu
Anu is a React framework developed by Teacher Stuart Zhengmei. Its features are as follows:
- support
React
Various new features of 16 - Run through nearly 800 official unit tests
- support
React
Buckets of - Support 99%
antd
component
These are developer-oriented features.
At the source level, the ANU architecture is similar to React V17, but only 1/3 the size of React. It is a great way to learn the React source code.
Let’s get started.
What are composite events and what are they useful for
React is a re-implementation of the capture -> Target -> bubble event mechanism in the browser.
Why reinvent the wheel on top of the browser event mechanism? The main reason is:
In the browser’s native implementation, an event is triggered to capture -> target -> bubble in the DOM tree.
The EVENT Handler will be invoked if the EVENT Handler is registered with the DOM that passes through during this process.
React does not operate DOM directly. Instead, React operates a virtual DOM tree (fiber tree) that maps to the DOM tree.
For example, for the following applications:
function App() {
return (
<div>
<p onClick={()= > console.log('click')}>click~</p>
</div>
) } ReactDOM.render(<App/>, root); Copy the code
DOM tree and Fiber tree are:
DOM tree: Fiber tree
html FiberRootNode
| |
body rootFiber
| | div App fiber | | p div fiber | p fiber Copy the code
As you can see, the DOM tree and fiber tree are not one-to-one.
The onClick handler is kept as props on fiber corresponding to p, not on the P DOM.
React needs to simulate the event transfer mechanism in the DOM tree and implement a similar mechanism to transmit events in the Fiber tree.
When the whole event mechanism is re-implemented, it is easy to add features to it, such as:
-
Smooth out the differences in event mechanics between browsers (IE is talking about you)
-
Customization requirements for events.
In React, for example, the change event in the form component is actually an input event in the native DOM.
In React, the Focus event is implemented by focusin and FocusOut in the native DOM.
- Priority mechanism.
In React, different events have different priorities. Setstates fired in event handlers for different events are executed with different priorities.
Implementation of composite events
The code for the following implementation comes from ANU.
The implementation of composite events is well understood:
-
Bind the Event Handler to the Document and listen for events through event delegates
-
When the event is triggered, obtain the DOM of the triggering event through E.target and find the fiber corresponding to the DOM
-
Walk from this fiber to the root fiber. Collect all fiber event handlers that bind events of this type during the walk and save them in the array Paths
-
Iterate through paths, calling event Handlers in turn to simulate the capture process
-
Iterate through paths.reverse(), calling the Event Handler in turn to simulate the bubbling process
Let’s take the click event as an example:
- call
addGlobalEvent('click')
Registered globalhandler
Used forEvent delegation
.
“DispatchEvent” is a handler.
function addGlobalEvent(name, capture) {
if(! globalEvents[name]) { globalEvents[name] = true;
// An implementation of addEventListener
addEvent(document, name, dispatchEvent, capture);
} } Copy the code
- When you click
DOM
To triggerdispatchEvent
.
function dispatchEvent(e, type, endpoint) {
e = new SyntheticEvent(e);
/ /... Some preprocessing, omitting
Renderer.batchedUpdates(function() { // 3. Collect click handlers along fiber paths through collectPaths let paths = collectPaths(e.target, terminal, {}); let captured = bubble + 'capture'; // 4. Simulate the capture process triggerEventFlow(paths, captured, e); if(! e._stopPropagation) { // 5. Simulate bubbling process triggerEventFlow(paths.reverse(), bubble, e); } }, e); } Copy the code
TriggerEventFlow is simply iterating through a set of numbers and executing a callback.
function triggerEventFlow(paths, prop, e) {
for (let i = paths.length; i--; ) {
let path = paths[i];
let fn = path.events[prop];
if (isFn(fn)) {
e.currentTarget = path.node; fn.call(void Awesome!, e); if (e._stopPropagation) { break; } } } } Copy the code
conclusion
Now we know that when we pass onClick props to a P component, the component itself is not bound to the corresponding handler, and there will be no Click Handler unbound after the component is destroyed.
The reason for “P corresponds to DOM responding to click events” is as follows:
The onClick callback on fiber corresponding to the DOM is collected in collectPaths in the dispatchEvent method and called in triggerEventFlow.