The content shared with you in this article is about the analysis of the React event system. It has certain reference value. Friends in need can refer to it.
There are two types of React event systems: synthetic events and native events.
When writing React components, it is easy for us to bind a synthetic event, but there is no way to bind the synthetic event of another component in one component. At this time, native events come in handy.
In addition to talking about mixed (mixed synthetic events and native events) events, event bubbling is also something we often need to deal with. This article will introduce it in combination with React.
1-1. React implements a SyntheticEvent (synthetic event) layer based on Virtual DOM, and the event handler we defined An instance of a SyntheticEvent object will be received, and the event bubbling mechanism is also supported. We can use stopPropagation() and preventDefault() to interrupt it.
1-2. All events are automatically bound to the outermost layer (document).
At the bottom of React, there are two main things done for synthetic events: Event delegation and automatic binding.
2-1. Event delegation Before using
React events, you must be familiar with its event proxy mechanism. It does not bind the event processing function directly to the real node, but binds all events to the outermost layer of the structure, using a unified event listener . This event listener A map is maintained to store all internal event listeners and handlers of the component. When a component is mounted or unmounted, some objects are just inserted or deleted on this unified event listener; when an event occurs, it is first processed by this unified event listener, and then the real event processing function is found in the mapping and called . This simplifies the event processing and recycling mechanism, and greatly improves efficiency.
2-2. Automatic binding
In a React component, the context of each method will point to the instance of the component, that is, automatically bind this to the current component. And React will also cache this reference to achieve CPU and memory optimization.
Native events can also be used under the React architecture. React provides a complete life cycle method. componentDidMount will be called after the component has been installed and the real DOM exists in the browser. At this time, we can complete the binding of native events.
But React does not automatically manage native events, so you need to log out of native events when uninstalling the component.
It is mentioned in the book (not too much introduction here):
Do not mix synthetic events with Mixing of native events
Use e.target judgment to avoid
The key point is the following paragraph, which is also the problem we need to focus on solving today :
Using reactEvent.nativeEvent.stopPropagation() to prevent bubbling will not work. The behavior of preventing React events from bubbling can only be used in the React synthetic event system, and there is no way to prevent native events from bubbling. On the contrary, preventing bubbling behavior in native events can prevent the propagation of React synthetic events.
Event bubbling mechanism:
Events bound through React, in their callback functions The event object is a SyntheticEvent synthesized by React, which is not the same thing as the native DOM event. To be precise, in React, e.nativeEvent is the event of the native DOM event.
React synthetic event and native event execution sequence diagram:
We can draw the following conclusions from the diagram:
(1) DOM React's synthetic event will be triggered only when the event bubbles to the document, so the e.stopPropagation of the React synthetic event object can only prevent the React simulated event from bubbling, but cannot prevent the real DOM event from bubbling
(2) DOM Preventing bubbling of events can also prevent synthetic events. The reason is that preventing bubbling of DOM events prevents events from propagating to the document.
(3) When both synthetic events and DOM events are bound to the document, React's processing The synthetic event should be put in first, so it will be triggered first. In this case, the stopImmediatePropagation of the native event object can prevent further triggering of the document DOM event
stopImmediatePropagation: If there are multiple event listening functions of the same type of event bound to the same element, when events of this type are triggered, they will be executed in the order in which they were added. If one of the listening functions executes the event.stopImmediatePropagation() method, the remaining listening functions will not be executed
(1) To prevent bubbling between synthetic events, use e.stopPropagation();
(2) To prevent synthetic events from being connected to the outermost document To prevent bubbling between events, use e.nativeEvent.stopImmediatePropagation();
(3) to prevent the bubbling of synthetic events and native events except the outermost document. This can be avoided by judging e.target. The code is as follows:
componentDidMount() { document.body.addEventListener('click', e => { if (e.target && e.target.matches('p.code')) { return; } this.setState({ active: false, }); }); }
7-1 Event registration
Event registration is to convert React events into DOM native events at the document node and register callbacks.
// enqueuePutListener 负责事件注册。 // inst:注册事件的 React 组件实例 // registrationName:React 事件,如:onClick、onChange // listener:和事件绑定的 React 回调方法,如:handleClick、handleChange // transaction:React 事务流,不懂没关系,不太影响对事件系统的理解 function enqueuePutListener(inst, registrationName, listener, transaction) { ... ... // doc 为找到的 document 节点 var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument; // 事件注册 listenTo(registrationName, doc); // 事件存储,之后会讲到,即存储事件回调方法 transaction.getReactMountReady().enqueue(putListener, { inst: inst, registrationName: registrationName, listener: listener }); }
Let’s look at the specific code of event registration and how to bind DOM native events on the document.
// 事件注册 // registrationName:React 事件名,如:onClick、onChange // contentDocumentHandle:要将事件绑定到的 DOM 节点 listenTo: function (registrationName, contentDocumentHandle) { // document var mountAt = contentDocumentHandle; // React 事件和绑定在根节点的 topEvent 的转化关系,如:onClick -> topClick var dependencies = EventPluginRegistry.registrationNameDependencies[registrationName]; for (var i = 0; i <p> Let’s look at the specific code for binding events to the bubbling phase: </p><pre class="brush:php;toolbar:false">// 三个参数为 topEvent、原生 DOM Event、Document(挂载节点) trapBubbledEvent: function (topLevelType, handlerBaseName, element) { if (!element) { return null; } return EventListener.listen(element, handlerBaseName, ReactEventListener.dispatchEvent.bind(null, topLevelType)); } // 三个参数为 Document(挂载节点)、原生 DOM Event、事件绑定函数 listen: function listen(target, eventType, callback) { // 去除浏览器兼容部分,留下核心后 target.addEventListener(eventType, callback, false); // 返回一个解绑的函数 return { remove: function remove() { target.removeEventListener(eventType, callback, false); } } }
In the listen method, we finally discovered the familiar addEventListener native event registration method. Only the document node will call this method, so only the document node has DOM events. This greatly simplifies DOM event logic and saves memory.
7-2. Event storage
After the event is registered, the callback function bound to the event needs to be stored. In this way, only after the event is triggered can we find the corresponding callback to trigger. In the initial code, we have seen that the putListener method is used to store event callbacks.
// inst:注册事件的 React 组件实例 // registrationName:React 事件,如:onClick、onChange // listener:和事件绑定的 React 回调方法,如:handleClick、handleChange putListener: function (inst, registrationName, listener) { // 核心代码如下 // 生成每个组件实例唯一的标识符 key var key = getDictionaryKey(inst); // 获取某种 React 事件在回调存储银行中的对象 var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {}); bankForRegistrationName[key] = listener; }
7-3. Event execution
Every time an event is triggered, the callback registered by addEventListener on the root node will be executed, that is, the ReactEventListener.dispatchEvent method, the event distribution entry function . The main business logic of this function is as follows:
Construct React synthetic events based on DOM events.
Put synthetic events into the queue.
Events in the batch queue (including those that have not been processed before, first in, first served)
Find the DOM and React Component triggered by the event
From this React Component, call the findParent method to traverse all parent components and store them in an array.
From this component to the last parent component, according to the previous event storage, use the React event name component key to find the corresponding binding callback method and execute it. The detailed process is:
The bubbling of React synthetic events is not really bubbling, but the traversal of nodes.
Personally, I think stopImmediatePropagation is very useful. It is necessary to prevent synthetic events from bubbling up to the DOM document. The reason is:
1.合成事件本来就绑定在document上,完全可以获取这个document 2.stopImmediatePropagation可以阻止触发的document DOM上的事件,这十分有必要 3.不会阻止DOM 上的事件冒泡到document DOM
Related recommendations:
Three commonly used value-passing methods in Vue
Implementation of React Event event registration
The above is the detailed content of An analysis of the React event system. For more information, please follow other related articles on the PHP Chinese website!