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:
- this Pointing Problem
- 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.