Why do event functions in React bind this?

Keywords: Javascript React github Attribute

The original is in my github Chinese, welcome to subscribe

We usually write React as follows:

class HelloMessage extends React.Component {

  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this); //Binding this
  }
  
  handleClick(){
    console.log( this )
  }

  render() {
    const { count } = this.state;
    
    return (
      <button onClick={ this.handleClick } >Hello</button>
    );
  }
}

ReactDOM.render(
  <HelloMessage/>,document.getElementById('root')
);

The above code works well. After handleClick binds this, it prints as follows:

HelloMessage {props: {...}, context: {...}, refs: {...}, updater: {...}, _reactInternalFiber: Hr, ...}
    context: {}
    props: {}
    refs: {}
    state: null
    updater: {isMounted: ƒ, enqueueSetState: ƒ, enqueueReplaceState: ƒ, enqueueForceUpdate: ƒ}
    _reactInternalFiber: Hr {tag: 1, key: null, elementType: ƒ, type: ƒ, stateNode: HelloMessage, ...}
    __proto__: b

Let's get rid of. bind(this) and print it again:

undefined

OK, sure enough, this is nothing.

But if you change handleClick to arrow function, you don't need bind(this):

 handleClick = () => {
    console.log( this )
 }

After the change, this can also be printed normally.

Let's first look at how the render method is compiled by Babel:

  render() {
    return React.createElement(
      'button',
      { onClick: this.handleClick },
      'Hello'
    );
  }

React creates DOM by simulating document.createElement with React.createElement method (of course, React creates virtual DOM). The onClick in the attribute points to this.handleClick method, which looks all right.

Next, we don't need React to achieve the above phenomenon of this:

// Create DOM
function createElement(dom, params) {
  var domObj = document.createElement(dom);
  domObj.onclick = params.onclick;
  domObj.innerHTML = 'Hello';
  document.body.appendChild(domObj);
}

// Class Demo
class Demo {

  handleClick() {
    console.log(this);
  }

  render() {
    createElement('button', {
      onclick: this.handleClick
    });
  }
}

// Implementation of render
new Demo().render();

Guess what's this pointing to here, buddies?

To print the results:

<button>Hello</button>

Aha ha ha, yes, this is not undefined, nor does it point to Demo objects, but the button DOM object itself created.

Now change handleClick to the arrow function:

// Class Demo
class Demo {

  handleClick = () => {
    console.log(this);
  }

  render() {
    createElement('button', {
      onclick: this.handleClick
    });
  }
}

// Implementation of render
new Demo().render();

Let's take a look at this print:

Demo {handleClick: ƒ}
    handleClick: () => { console.log(this); }
    __proto__: Object

The groove actually points to the Demo object, so that you can freely access the properties and methods in Demo in handleClick!

Explain

Actually, if you have a solid foundation in JavaScript, I don't think you'll come in to read this article.
The problem here involves only two points:

  1. this Pointing Problem
  2. Characteristic of Arrow Function

this Pointing Problem

I bet that the above example of Demo, which many people see in the clouds and mists, turns it into ES5:

function createElement(dom, params) {
  var domObj = document.createElement(dom);
  domObj.onclick = params.onclick;
  domObj.innerHTML = 'Hello';
  document.body.appendChild(domObj);
}

// Create a Demo class, which corresponds to the writing of class Demo {}
function Demo() {

}

// Mount handleClick method on Prototype
Demo.prototype.handleClick = function() {
   console.log(this);
};

Demo.prototype.render = function() {
  createElement('button', {
    onclick: this.handleClick
  });
};

new Demo().render(); // Running render method

Print results:

<button>Hello</button>

Seriously note: When defining methods in ES6 class, if not arrow functions, the methods are mounted on prototype prototype objects!

Next, write handleClick as an arrow function:

function createElement(dom, params) {
  var domObj = document.createElement(dom);
  domObj.onclick = params.onclick;
  domObj.innerHTML = 'Hello';
  document.body.appendChild(domObj);
}

// Create a Demo class, which corresponds to the writing of class Demo {}
function Demo() {
    // If you don't know the arrow function, please make up for it.
    var _this = this;
    this.handleClick = function(){
        console.log( _this );
    }
}

Demo.prototype.render = function() {
  createElement('button', {
    onclick: this.handleClick
  });
};

new Demo().render(); // Running render method

Then print:

Demo {handleClick: ƒ}
    handleClick: ƒ ()
    __proto__: Object

Ha-this pointed to the desired object environment.

summary

Through the above code parsing, we can know that in React, when the arrow function is not applicable, we need to point this in the function to the current object through bind in order to access it properly.

When using the arrow function, because of the characteristics of the arrow function, this in the function is the current object context, so there is no need for bind to point to.

If there is something wrong, please correct it. Thank you.

Posted by Beatnik on Tue, 17 Sep 2019 02:10:38 -0700