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:

  • supportReactVarious new features of 16
  • Run through nearly 800 official unit tests
  • supportReactBuckets of
  • Support 99%antdcomponent

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:

  1. Smooth out the differences in event mechanics between browsers (IE is talking about you)

  2. 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.

  1. 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:

  1. Bind the Event Handler to the Document and listen for events through event delegates

  2. When the event is triggered, obtain the DOM of the triggering event through E.target and find the fiber corresponding to the DOM

  3. 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

  4. Iterate through paths, calling event Handlers in turn to simulate the capture process

  5. Iterate through paths.reverse(), calling the Event Handler in turn to simulate the bubbling process

Let’s take the click event as an example:

  1. calladdGlobalEvent('click')Registered globalhandlerUsed 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
  1. When you clickDOMTo 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.