“This is the second day of my participation in the First Challenge 2022.


When the React class component uses functions in JSX, it usually needs to bind the function to the current this. We know that this is not a React problem, but the default binding in Javascript. But in addition to how functions are called in JavaScript, I’d like to know more about how JSX’s bind events are performed in the Reac event system, and why React doesn’t help users automatically bind component instances.

Issue review

In JSX bind event -> to event trigger, because saving the call as a callback function causes the default bind to fall back and this to be lost

class TestComponent extends React.Component {
  test() {
    console.log(this) // undefined
  }
  render() {
    return (
    	<button onClick={this.test}>test this</button>)}}Copy the code

The analysis reason

Implicit binding in JavaScript

Ordinary function calls

Normal function calls. In non-strict mode, the function’s this points to the window object by default, and in strict mode, undefined

function testThis() {
    console.log(this)
}
testThis() // window
Copy the code
"use strict";
function testThis() {
    console.log(this)
}
testThis() // undefined
Copy the code

Class method call

Note that class declarations are executed in strict mode, including constructors, static methods, prototype methods, getters, and setters.

View details on MDN

class TestThis {
  name = 'testName'
    test() {
    console.log(this.name)
  }
}

let test = new TestThis()
test.test() // testName
Copy the code

So how did this get lost in JSX? The following code explains why this is missing

class TestThis {
  name = 'testName'
    test() {
    console.log(this)}}let testInstance = new TestThis()
let testFn = testInstance.test
testFn() // undefined
Copy the code

How to solve

The solution Recommend index Recommend index
Render the bind in this Render executes bind multiple times 🌟
Arrow function in render Render generates new functions multiple times 🌟 🌟
In the constructor to bind this Constructor is not necessary sometimes 🌟 🌟 🌟
Arrow function when defining a class method recommended 🌟 🌟 🌟 🌟

How does React call the event callback

The above explanation explains why this is missing in Javascript calls. Is this missing in JSX to bind events because of such calls?

From JSX to fiber

How does testThis bind from JSX to being executed

  1. First of all,JSX-> Turn to BabelReact.creatElement
class TestComponent extends Component {
  testThis() {
    console.log(this)}render() {
    return (
      <button onClick={this.testThis}>test this</button>)}}Copy the code
class Component {
  testThis() {
    console.log(this);
  }

  render() {
    return React.createElement("button", {
      onClick: this.testThis
    })
  }
}
Copy the code
  1. React.createElement -> Convert the react.element object

  1. React. element object -> Fiber object

From triggering to executing the event callback

React will be the first timecompleteWorkStage according to thefiberCreate a real DOM and register all events that need to be registered in the DOMThe root elementThe above.

Here’s how React bubbles testThis when a button is clicked.

First we click the button, and the listener is bound to the dispatchDiscreteEvent function. Listener is the dispatchDiscreteEvent function for discrete events. Click is a discrete event. But whatever the event is, dispatchEvent is eventually called, and dispatchEvent is used to distribute the event.

What does dispatchEvent do?

Call attemptToDispatchEvent

  • Find the actual triggering dom element based on the nativeEvent object nativeEvent
export function attemptToDispatchEvent(domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, targetContainer: EventTarget, nativeEvent: AnyNativeEvent,) {
  // find the actual triggering dom element based on the nativeEvent object nativeEvent
  const nativeEventTarget = getEventTarget(nativeEvent);
  // Get the corresponding fiber to trigger
  // React generates a random pointer to the corresponding fiberNode when generating the real DOM, so fiber can be found through dom
  let targetInst = getClosestInstanceFromNode(nativeEventTarget);
  // Ignore some code...
  
  // Finally call the event handler plug-in event system
  dispatchEventForPluginEventSystem(
    domEventName,
    eventSystemFlags,
    nativeEvent,
    targetInst,
    targetContainer,
  );
  return null;
}
Copy the code

DispatchEventForPluginEventSystem invokes batchedUpdates batch execution dispatchEventsForPlugins, DispatchEventsForPlugins finally call extractEvents

What does extractevents mainly do

  • Create composite events based on different types of events
  • Collect listeners for each level of compliant events (fiberThe body of thetestThis)
  • The last generationdispatchQueuetoprocessDispatchQueueCalls to perform

ProcessDispatchQueue will cooperate with processDispatchQueueItemsInOrder simulate the bubbling or capture the listener, InvokeGuardedCallback eventually calls callCallback, but since invokeGuardedCallback doesn’t pass the context to callCallback, So func. Apply (context, funcArgs) is undefined

thinking

View the source code under review process, in fact in the plug-in event system (dispatchEventForPluginEventSystem) can take to the instance of a component, if preserved examples here, Is it possible to resolve the problem that the apply context is undefined when callCallback is executed?

conclusion

React execute event callback When onClick is triggered, the React listener on the root element will be invoked, and the listeners will be called on the plugins. The listeners will collect the functions callback on fiber based on different events and put it into the listeners array to form the dispatchQueue. Simulates bubbling to execute event callbacks in listeners.

Summary: The testThis event was saved as a callback when the listener was collected, causing this to be lost, and undefined is bound when the listener is executed by apply, resulting in undefined