The cause of

The React event handler function was shown to me in two different ways and I asked some questions, so I looked up some documentation…

On the left is the most common way to write React. We all know that the use of React is to use the arrow function, which binds the outer layer by defaultthisThe one on the right looks a little complicated at first glance

I’m trying to figure out

  1. whyReactNeed to bindthisWhy does not bind get an errorthisforundefined)
  2. Binding approach

knowledge

Let’s review a few points first

Relationships among constructors, prototypes, and instances

// constructor
function Point(x, y) {
  this.x = x;
  this.y = y;
}
// Prototype method
Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ') ';
};
/ / the new instance
var p = new Point(1.2);
Copy the code

ES6 Class keyword

Combinatorial inheritance (constructor + prototype chain)

ES6 classes can be thought of as syntactic sugar, written to make object prototype writing clearer and more like object-oriented programming syntax. The above code is rewritten using ES6 classes, as follows.

/ / define the class
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ') '; }}Copy the code

The Point class defines a toString method in addition to its constructor. In fact, all methods of a class are defined on the prototype property of the class.

class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...}}/ / is equivalent to

Point.prototype = {
  constructor() {},
  toString() {},
  toValue(){}};Copy the code

Strict mode

The interior of classes and modules, by defaultStrict mode, no need to useuse strictGlobal scope is not specified in strict modethis

This points to the

  • fn()We can view this aswindow.fn(), sothis === window
  • obj.fn(), it isobjI called the function, which is inside the functionthis === obj
  • In order toThe constructorWhen the form is called, this is generatedInstance objects
  • apply.call.bindCall, this isSpecified object

There are three ways to modify the reference to this

  • call: fn.call(target, 1, 2)Specify this in the function andCall a function
  • apply: fn.apply(target, [1, 2])withcall, difference: withAn array ofGets parameters in the form of
  • The bind: fn. Bind (target) (1, 2)Specify this in the function,And return the functionTo permanently bind a functionThis value

Arrow function

One feature: the arrow function binds the value of this by default, so the value of this in the arrow function is the same as the value of this in the outer this

The this pointer is missing

This is a JavaScript language issue, not React. Look at an example

let obj = {
    tmp:'Yes! '.testLog:function(){
        console.log(this.tmp); }}; obj.testLog();/ / Yes!
// this points to obj and can output TMP property normally;
Copy the code

Modify the code:

let obj = {
    tmp:'Yes! '.testLog:function(){
        console.log(this.tmp); }};let tmpLog = obj.testLog; // Intermediate variable
tmpLog(); // undefined
// Instead of calling the testLog method in the obj object directly, a tmpLog transition is used
// When tmpLog() is called, the reference to this in the method is lost, and the default is to refer to window
// window.tmp is undefined;
Copy the code

In JavaScript, if you pass a function name to a variable and then call the method by enclosing parentheses () after the variable, the reference to this inside the method is lost.

React Code Analysis

The React component Class uses the ES6 Class to define a component Class that generates a component instance when it is called from another component or rendered to the interface using the reactdom.render () method. Based on the basic rule that this points to, this here will eventually point to an instance of the component.

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
 
  handleClick(){
    console.log('this is:'.this);
  }
 
  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>); }}Copy the code

When a component instance is generated, the constructor is executed. Here’s a look at this line of code:

this.handleClick = this.handleClick.bind(this)
Copy the code

The expression to the right of the assignment looks for the this.handleClick method, where this points to the newly generated instance. The prototype handleClick method is found, and bind(this) generates a new method that specifies this. Assign the newly generated function to the instance’s handleClick property, where the handleClick property is generated directly as an instance property, as the object’s assignment mechanism shows.

Summary: Take the prototype approachhandleClick()Add as instance methodhandleClick(), and specify the values in this methodthisPermanently points to the current instance.

The onClick method is called when the user clicks. Since onClick is an intermediate variable, the this pointer in the handler handleClick is lost.

The interior of the Class is forced to run in strict mode. This is not specified as window, but undefined. Therefore, if this.handleclick = this.handleclick.bind (this); If the handleClick method executes with this undefined, an error will be reported if the property of this is accessed.

React binds this in 4 ways

  1. Used in constructorsbindThe bindingthis
class Button extends React.Component {

  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick(){
    console.log('this is:'.this);
  }
  
  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>); }}Copy the code
  1. Used when calledbindThe bindingthis
class Button extends React.Component {

  handleClick(){
    console.log('this is:'.this);
  }
 
  render() {
    return (
      <button onClick={this.handleClick.bind(this)}>
        Click me
      </button>); }}Copy the code
  1. Use the arrow function binding when calledthis
class Button extends React.Component {
 
  handleClick(){
    console.log('this is:'.this);
  }
 
  render() {
    return (
      <button onClick={()= >this.handleClick()}>
        Click me
      </button>); }}Copy the code
  1. Syntax binding using property initializerthis
class Button extends React.Component {
 
  handleClick=() = >{
    console.log('this is:'.this);
  }
 
  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>); }}Copy the code

Methods 2 and 3 bind this at call-time. Advantages: It is simpler to write, and there is no need to add a class constructor to bind this when there is no state in the component

Disadvantages: Each call generates a new method instance, so there is an impact on performance, and when this function is passed as a property value to lower-level components, those components may do additional re-rendering because each time a new method instance is passed as a new property.

Methods 1 and 4 are bound once and called multiple times

Method 4: initialize the method as an arrow function, so we bind this when we define the function. We don’t need to bind this in the class constructor, and we don’t need to bind it when we call it. It combines the advantages of mode 1, mode 2 and mode 3, but requires Babel translation

summary

Mode 1 is the recommended binding mode and has the best performance. Methods 2 and 3 have a performance impact and can cause re-shading issues when methods are passed as attributes to child components. Method 4 is the best binding method and requires bable translation

Whenever a parameter needs to be passed where it is called, it must be used where the event is calledbindOr arrow functions, there’s no solution to that.


The attached

  • React this points to the problem
  • React event handler this points to undefined
  • 5 ways to bind this to React
  • Why bind this to the React component method
  • The basic syntax of CLASS
  • Strict mode
  • conclusion