1 React16 Event delegate

1.1 the document

In versions prior to Act17, events bound to the virtual DOM were placed into an event pool

And then React globally just binds the document to this event, and it listens for event bubbling events in that event

React finds the corresponding DOM element in the event pool and triggers the corresponding event

1.2 validation

How to verify that all React events are bound to document?

Here is an example of binding an event to the Document before the React register (making sure its own event fires before the React document event), and then binding an event and a React composite event to the actual DOM.

After the page is rendered, click the button to verify

  • Prepare aReactcomponent
export default function () {

  const buttonRef = useRef() as React.MutableRefObject<HTMLButtonElement>;

  useEffect(() = > {
    buttonRef.current.addEventListener('click'.function (e) {
      console.log('Real Button DOM event')})}, [])const handleClick = useCallback(() = > {
    console.log('Button events bound to the virtual DOM')}, [])return <button ref={buttonRef} onClick={handleClick}>button</button>

}
Copy the code

Then bind an event to the document before the React dependency is inserted

After clicking the button, the console prints the following

Events on the real Button's DOM event Document are bound to button events on the virtual DOMCopy the code

This validates React’s mechanism for mounting all events to document


You can even prevent the event from bubbling in a real event, and then see that neither the Document event nor the React event fires

After clicking the button, the console prints the following

Real Button DOM eventsCopy the code

1.3 How To Implement it

After the Event bubbles onto the Document, there is a path parameter in the callback Event that the Event triggers

It describes the bubbling sequence in detail

1.4 Function trigger Parameters

Take a look at the object differences between the React event-bound callback and the real DOM binding callback

button.addEventListener('click'.function (e) {
  console.log(e)
})
Copy the code
<button 
  id="button"
  onClick={(e) = >{
    console.log(e)}} > </button>Copy the code

As you can see, the callbacks are different

In TypeScript, the two types are also different

/ / true dom
button.addEventListener('click'.function (e: MouseEvent) {})

// React Synthesizes events
function(e:React.MouseEvent<HTMLButtonElement>){}
Copy the code

If you want to get the native MouseEvent in the synthesized event parameter, you can access this parameter

e.nativeEvent
Copy the code

1.5 persist

React does one thing to maximize event binding performance when a bound event ends

The synthesized event callback object is destroyed immediately. The following example can prove this

const handleClick = useCallback((e) = > {
  console.log(e)
  setTimeout(() = > console.log(e))
}, [])
Copy the code

You can call E.pineist during event execution to save the state

const handleClick = useCallback((e) = > {
  e.persist();
  console.log(e)
  setTimeout(() = > console.log(e))
}, [])
Copy the code

2 This in React

2.1 this disappear

After composing events, let’s look at why events are bound to this in the class component

First, in JS, this points to its caller as follows

const obj = {
  name: 'obj'.func() {
    console.log(this)
  }
}

obj.func()  // {name: "obj", func: ƒ}
Copy the code

If func is extracted and executed, then func prints undefined in modular or strict mode

const func = obj.func;

func();   // undefined
Copy the code

Then this operation is familiar. The component code is as follows

class App extends Component {

  handleClick() {
    console.log('handleClick')}render() {
    return (
      <button onClick={this.handleClick}>button</button>); }}Copy the code

HandleClick in this.Handleclick is extracted and placed in an event pool [……]

And then it gets fired, and at that point, this is no longer referring to that component

2.2 Binding the this pointer

Previously, there were three more options for this binding, and all of them had advantages and disadvantages

  • Virtual DOM directbindThe binding
<button onClick={this.handleClick.bind(thisButton)} > < / button >Copy the code

Drawback: Functions bind once every time a component is updated, affecting performance

  • Constructor binding
constructor(){
	this.handleClick = this.handleClick.bind(this)}Copy the code

Disadvantages: Bind every function once, resulting in bloated code

  • Bind an anonymous function directly
<button onClick={() = >thisButton. HandleClick ()} > < / button >Copy the code

Defect: the combination of the above two defects

2.3 Arrow Function

The arrow function’s this pointer always points to its function’s position, as follows

const obj = {
  name: 'obj'.func() {
    const exec = () = > console.log(this);
    exec();
  }
}

obj.func();   // {name: "obj", func: ƒ}}
Copy the code

So, you can write this in the component

class App extends Component {

  handleClick = () = > {
    console.log('handleClick')}render() {
    return (
      <button onClick={this.handleClick}>button</button>); }}Copy the code

3 React 17 Event delegation

Upgrade the React version of your project to version 17

$ yarn add react@17.0.1 react-dom@17.0.1
Copy the code

The React version will be updated to 17

React16 is executed in the following order

Events on the real Button's DOM event Document are bound to button events on the virtual DOMCopy the code

So the order of execution of act17 becomes this

The real BUTTON DOM event is bound to the button event in the virtual DOMdocumentEvents on theCopy the code

This is because Act17 supports multiple React versions on a page, which can cause problems if all events are tied to a total document

So, on version 17, the event delegate is not placed on the document, but on the root node of the execution, such as #root

ReactDOM.render(
  <HelloWorld />.document.getElementById('root') as HTMLElement
);
Copy the code