Preliminary understanding and validation of event mechanisms
Representational understanding, validation, meaning and reflection on react event mechanism.
First, the original event review
Before we dive into the React event mechanism, let’s briefly review a few important things about JavaScript native events:
1. Event capture
When an element fires an event (such as onclick), the top-level object Document emits a stream of events that follow the nodes of the DOM tree to the target element node stream until it reaches the target element where the event actually occurred. In this process, the listener function corresponding to the event is not fired.
2. Event target
When the target element is reached, the corresponding handler for the target element event is executed. If no listener function is bound, it is not executed.
3. Events bubble up
Start with the target element and propagate to the top-level element. If any node is bound to the corresponding event handler, these functions will be fired once. To prevent bubbling, you can use e.topPropagation () or e.ancelbubble =true (IE) to prevent bubbling.
4. Event delegate/event agent
The simple idea is to delegate a response event to another element. When the child node is clicked, the click event bubbles up, and when the parent node catches the event, we determine if it is the desired node and process it. The advantages are reduced memory consumption and dynamically bound events.
2. Preliminary understanding of event mechanism
React implements its own event mechanism, including event registration, event composition, event bubbler, event dispatch, etc. React implements its own event mechanism, including event registration, event composition, event bubbler, event dispatch, and so on.
We all know that react events are not bound to a specific DOM node, but to a Document, which is then handled by a unified event handler, which is also browser-based event mechanism (bubbling). All node events are triggered on the Document.
1. What do we write about in JSX events?
Let’s write the React JSX syntax with the click event. What does it look like?
class Index extends React.Component{
handerClick= (value) = > console.log(value)
render(){
return <div>
<button onClick={ this.handerClick } >Click on the button</button>
</div>}}Copy the code
Babel converts to the react. createElement form as follows:
The event is stored in the React. CreateElement props
Finally, the fiber object looks like this:
The memoizedProps and pendingProps on the Fiber object hold our events.
2. Where are events bound to
Next we do something 😂😂😂, add an input box to the demo project and bind an onChange event. Open your eyes and see what happens next?
class Index extends React.Component{
componentDidMount(){
console.log(this)
}
handerClick= (value) = > console.log(value)
handerChange=(value) = > console.log(value)
render(){
return <div style={{ marginTop:'50px'}} >
<button onClick={ this.handerClick } >Click on the button</button>
<input placeholder="Please enter the content" onChange={ this.handerChange} / >
</div>}}Copy the code
So let’s take a lookinput dom
The events bound to the element
And then let’s seedocument
The events bound to
We found that the onChange that we bound to was not bound directly to the input, but to the document, and then our onChange was handled as a bunch of event listeners, Such as blur, change, input, keyDown, keyUp, etc.
To sum up, we can come to the conclusion that:
- The events we bound in JSX (handerClick, handerChange in demo) were not registered with the real DOM at all. Is bound to document unified management.
- â‘¡ The real DOM click event is handled separately and has been replaced with an empty function by the React layer.
- 3. Events bound to React, such as onChange, may have multiple events corresponding to document.
- React does not initially bind all events to document. Instead, it takes an on-demand binding, such as the onClick event, and unbinds the Document Click event.
React events and native events
Consider this:
If both synthetic and native events are bound on a node, what about a no-bubbling relationship?
The answer is already there. Let’s now analyze this relationship based on what we know so far.
Because composite events are triggered based on the browser’s event mechanism, they bubble up to the top-level element through the bubbling mechanism, which is then handled by dispatchEvent.
- Conclusions reached:
Native event preventing bubbling definitely prevents synthetic events from firing.
Preventing bubbling for composite events does not affect native events.
Why is that? Remember the browser event mechanism
Browser events are executed through three phases: capture phase – target element phase – bubbling phase.
Native events on the node are executed in the target phase, whereas synthesized events are executed in the bubbling phase, so native events are synthesized first and then bubbled to the parent node.
Since the native prevent bubbling, then the composition of the implementation of what.
Ok, it’s the turn of the composition to be prevented from bubbling, will the native be implemented? Of course I can.
Because the native event precedes the execution of the composite, only the composite event is prevented from bubbling within the composite event.
So the conclusion is drawn:
-
Native events (which prevent bubbling) prevent synthetic events from executing
-
Synthetic events (which prevent bubbling) do not prevent native events from executing
It is best not to mix the two to avoid some strange problems
Here’s a simple example to look at the sequence of React events and native events:
In React, composited events are bound to the top layer of components in the form of event delegates and automatically destroy bound events during the component unmount phase.
class App extends React.Component<any.any> {
parentRef: any;
childRef: any;
constructor(props: any) {
super(props);
this.parentRef = React.createRef();
this.childRef = React.createRef();
}
componentDidMount() {
console.log("The React componentDidMount!");
this.parentRef.current? .addEventListener("click".() = > {
console.log("Native Event: Parent ELEMENT DOM event listener!");
});
this.childRef.current? .addEventListener("click".() = > {
console.log("Native Event: Child element DOM event listener!");
});
document.addEventListener("click".(e) = > {
console.log("Native Event: Document DOM Event Listener!");
});
}
parentClickFun = () = > {
console.log(React event: Parent element event listener!);
};
childClickFun = () = > {
console.log(React event: Child element event listener!);
};
render() {
return (
<div ref={this.parentRef} onClick={this.parentClickFun}>
<div ref={this.childRef} onClick={this.childClickFun}>Analyze the sequence of events</div>
</div>); }}export default App;
Copy the code
After the event is triggered, you can see console output:
Native event: Child element DOM event listener! Native event: Parent element DOM event listener! React events: Child element events listen! React events: Parent element events listen! Native Event: Document DOM event listener!Copy the code
Through the above process, we can understand:
- React all events are mounted on
document
On the object; - When a real DOM element fires an event, it bubbles to
document
Object, then process the React event; - So the React event is handled after the native event is executed;
- And finally actually execute
document
Mount events on.
1. e.stopPropagation
Developers would prefer to use e.topPropagation () to prevent bubblings of current DOM events. In fact, e.topPropagation () can only prevent bubblings between composite events, the underlying composite events, as described in the previous two sections. Composite events that do not bubble to the top. The event itself is also executed on document. So the best you can do is prevent the Document event from bubbling up to the Window.
class App extends React.Component<any.any> {
parentRef: any;
childRef: any;
constructor(props: any) {
super(props);
this.parentRef = React.createRef();
}
componentDidMount() {
this.parentRef.current? .addEventListener("click".() = > {
console.log("Prevent native events from bubbling ~");
});
document.addEventListener("click".(e) = > {
console.log("Native Event: Document DOM Event Listener!");
});
}
parentClickFun = (e: any) = > {
e.stopPropagation();
console.log("Prevent composite events from bubbling ~");
};
render() {
return (
<div ref={this.parentRef} onClick={this.parentClickFun}>Click to test whether composite events and native events can be mixed</div>); }}export default App;
Copy the code
Output result:
Prevent native events from bubbling ~ Prevent synthetic events from bubbling ~Copy the code
2. Whether composite events and native events can be mixed
It is best not to mix composite and native events. If the stopPropagation method is executed in the native event, other React events will become invalid. Because all the elements’ events will not bubble up to the document. React events will not be registered. Take a look at the code:
class App extends React.Component<any.any> {
parentRef: any;
childRef: any;
constructor(props: any) {
super(props);
this.parentRef = React.createRef();
}
componentDidMount() {
this.parentRef.current? .addEventListener("click".(e: any) = > {
e.stopPropagation();
console.log("Prevent native events from bubbling ~");
});
document.addEventListener("click".(e) = > {
console.log("Native Event: Document DOM Event Listener!");
});
}
parentClickFun = (e: any) = > {
console.log("Prevent composite events from bubbling ~");
};
render() {
return (
<div ref={this.parentRef} onClick={this.parentClickFun}>Click to test whether composite events and native events can be mixed</div>); }}export default App;
Copy the code
Output result:
Prevents native events from bubbling ~Copy the code
Four, meaning,
What’s the point of react doing so much by itself?
- Reduce memory consumption, improve performance, don’t need to register so many events, an event type is registered on document only once
- Unified specification, solve IE event compatibility problem, simplify event logic
- Be developer friendly
Understanding of composition
First, concept introduction
A React SyntheticEvent is an event object that emulates all the capabilities of native DOM events in React, a cross-browser wrapper for browser-native events. It defines synthesized events according to the W3C specification, is compatible with all browsers, and has the same interface as browser native events.
In React, events such as onClick are not native events, but react events synthesized from native events, such as the Click event synthesized into an onClick event. Such as blur, change, input, keyDown, keyup, etc., synthesized into onChange.
Here’s a simple example:
const button = <button onClick={handleClick}>Leo button</button>
Copy the code
In React, all events are synthesized and not native DOM events, but DOM events can be obtained through the E.ativeEvent property.
const handleClick = (e) = > console.log(e.nativeEvent);;
const button = <button onClick={handleClick}>Leo button</button>
Copy the code
When learning something new, it’s important to know why the technology is there. So why does React use composite events? It has three main purposes:
- Browser compatibility, to achieve better cross-platform
React uses a top-level event broker mechanism that ensures consistent bubbling and can be executed across browsers. React provides compositing events to smooth out differences between browser event objects and simulate compositing events across platforms.
- Avoid garbage collection
Event objects can be created and reclaimed frequently, so React introduces an event pool from which event objects can be obtained or released. React event objects are not released, but stored in an array that is ejected from when the event is triggered, avoiding frequent creation and destruction (garbage collection).
- Facilitate unified event management and transaction mechanisms
React SyntheticEvent is an example of how to implement React SyntheticEvent.
2. Differences between synthetic events and native events
React events are similar to native events, but not identical. Here are a few common differences:
1. Event names are named in different ways
React events are named in a small camelCase (onclick, onblur, etc.) :
// Native event binding
<button onclick="handleClick()"</button>React Synthesizes the event binding mode
const button = <button onClick={handleClick}>Leo button naming</button>
Copy the code
2. Event handlers are written differently
The React JSX syntax passes in a function as an event handler.
// Native event handler
<button onclick="handleClick()"</button>// React synthesizes the event handler
const button = <button onClick={handleClick}>Leo button naming</button>
Copy the code
3. The default blocking behavior is different
In the native event, it is possible to prevent the default behavior by returning false, but in React, the explicit preventDefault() method is required. Here is an example of preventing the TAB from opening a new page by default.
// Native events block the default behavior
<a href="https://www.pingan8787.com"
onclick="Console. log('Leo prevents native events ~'); return false"> Leo prevents native events </a>The React event blocks the default behavior
const handleClick = e= > {
e.preventDefault();
console.log('Leo prevents native events ~');
}
const clickElement = <a href="https://www.pingan8787.com" onClick={handleClick}>Leo prevents native events</a>
Copy the code
4. Summary
Summary of the previous differences:
Native events | The React event | |
---|---|---|
Event name Naming method | All lowercase names (onclick, onblur) |
The name is small hump (onClick, onBlur) |
Event handler syntax | string | function |
Block the default behavior | Event to returnfalse |
usee.preventDefault() methods |
The event pool of composite events
1. Event pool introduction
The React event system provides performance optimization by synthesizing event object pools. Synthetic event objects are managed uniformly in the event pool, and different types of synthetic events have different event pools.
- When the event pool is full, React creates new event objects and sends them to the component.
- When the event pool is full, React reuses event objects from the event pool and sends them to components.
For a question about how event pooling works, take a look at the following image:
2. Event Pool Analysis (React 16)
React event pools are supported only in React 16 and earlier versions. React 17 does not use event pools. The React 16 version is used as an example.
function handleChange(e) {
console.log("Raw data:", e.target)
setTimeout(() = > {
console.log("Scheduled task E.target:", e.target); // null
console.log("Scheduled task: E:", e);
}, 100);
}
function App() {
return (
<div className="App">
<button onClick={handleChange}>Test event pool</button>
</div>
);
}
export default App;
Copy the code
You can see the output:
For a one-click event handler, printing e.target in the normal function execution context points to the DOM element, but printing null in setTimeout. If this were not the React event system, it would print the same. Because in the React to the concept of an event pool, every time we use the event source object, after the event function, can through such means as releaseTopLevelCallbackBookKeeping will release source object to the events in the pool, the benefits of this every time we don’t have to create an event source object, You can take an event source object from the event pool and reuse it. After the event handler completes execution, the event source is released into the event pool and the properties are emptied. This is why null is printed in setTimeout.
In React 16 and earlier versions, all properties of synthesized event objects are set to NULL after all event handlers are called. In this case, if we need to retrieve the properties of the event object after the event handler has run, we can use the React provided e.pineist () method to preserve all properties:
// Change only the handleChange method
function handleChange(e) {
// Only add persist() execution
e.persist();
console.log("Raw data:", e.target)
setTimeout(() = > {
console.log("Scheduled task E.target:", e.target); // null
console.log("Scheduled task: E:", e);
}, 100);
}
Copy the code
Here are the results:
3. Event Pool Analysis (React 17)
React V17 doesn’t have a lot of changes overall, but it does have a lot of changes to the event system. First of all, many of the above implementation functions no longer exist in v17. Let me briefly describe the revamp of the V17 event system.
1 Bind the event to reactdom. render(app, container); Rather than document, this benefit is in favor of the micro front end, a micro front end system may have multiple applications, if continue to take all binding indocument
Up, so there may be problems with more applications.
2 Align native browser events
React 17 finally supports native event capture, aligning with browser native standards. At the same time, the onScroll event no longer bubbles. OnFocus and onBlur use native FocusIn, focusOut compositing.
React 17 Disable the reuse of the event pool. When setTimeout is displayed, e.target cannot be found.
4. Common problems
1. In the React event, this indicates a problem
In React, the JSX callback does not bind this by default. This. FunName is undefined:
class App extends React.Component<any.any> {
childClickFun = () = > {
console.log("The React events");
};
clickFun() {
console.log(React this points to the problem.this.childClickFun); // undefined
}
render() {
return (
<div onClick={this.clickFun}>React this points to the problem</div>); }}export default App;
Copy the code
There are two ways to solve this problem:
- use
bind
Methods the bindingthis
 :
class App extends React.Component<any.any> {
constructor(props: any) {
super(props);
this.clickFun = this.clickFun.bind(this);
}
// omit other code
}
export default App;
Copy the code
- Will need to use
this
Rewrite the method to useArrow functionDefinition:
class App extends React.Component<any.any> {
clickFun = () = > {
console.log(React this points to the problem.this.childClickFun); // undefined
}
// omit other code
}
export default App;
Copy the code
Or use the arrow function in the callback function:
class App extends React.Component<any.any> {
// omit other code
clickFun() {
console.log(React this points to the problem.this.childClickFun); // undefined
}
render() {
return (
<div onClick={()= >This.clickfun ()}>React This points to the problem</div>); }}export default App;
Copy the code
2. Pass parameters to events
In React, you can use two methods to pass parameters to events:
const List = [1.2.3.4];
class App extends React.Component<any.any> {
// omit other code
clickFun (id) {console.log('Current click:', id)}
render() {
return (
<div>
<h1>The first is to pass the parameter by binding this</h1>
{
List.map(item => <div onClick={this.clickFun.bind(this, item)} >Button: {item}</div>)}<h1>The second way is to pass the parameter by binding this to the arrow function</h1>
{
List.map(item => <div onClick={()= >This.clickfun (item)}> button: {item}</div>)}</div>); }}export default App;
Copy the code
The two approaches are equivalent:
- The first way through
Function.prototype.bind
Implementation; - The second is implemented through the arrow function.
Reference article:
Segmentfault.com/a/119000003…
28 of1 toutiao. IO/posts /…
Juejin. Cn/post / 695563…