If we go back into the time where the hooks were not yet born, we will inevitably encounter this code while creating a React class component.
class MyClassComponent extends React.Component {
constructor(props) {
super(props);
// Assuming that you need to pass this as a prop to one of a child component
this.someMethod = this.someMethod.bind(this); // But why? 🤔
}
...
}
This is because whenever inside a class component when we need to pass a function as props to the child component, we have to do one of the following:
- Bind it inside the constructor function.
- Bind it inline (which can have some performance issues).
- Use arrow function (which is the same as property initializer syntax).
Have you ever wondered why it has to be this way? Why we have to do this extra piece of work?
Through this article, I will first try to explain the binding inside the constructor function. Once we acquire that knowledge, we will try to answer why arrow functions don’t follow the same ceremony.
One thing we need to know that binding in the constructor has nothing to do with React. It’s purely related to how JavaScript implements this. Let’s look at the following code:
var x = 10;
let foo = {
x: 90,
getX: function() {
return this.x;
}
};
foo.getX(); // prints 90
let xGetter = foo.getX;
xGetter(); // prints 10;
When we initialised x into a global scope, it becomes the property of the window object (assuming that it’s a browser environment and not a strict mode). We can assert that:
window.x === 10; // true
this will always point to the object onto which the method was invoked. So, in the case of foo.getX(), this points to foo object returning us the value of 90. Whereas in the case of xGetter(), this points to window object returning us the value of 10.
To retrieve the value of foo.x, we can create a new function by binding the value of this to foo object using Function.prototype.bind.
let getFooX = foo.getX.bind(foo);
getFooX(); // prints 90
Armed with this knowledge, let’s try to understand what happens when you pass a function prop into the child component.
In the following code example, we have created a dummy class component to mimic React Component mental model. Inside the render function, we are returning a plain JS object which has a functional prop called 'onClick'.
The React element is just an immutable description object with
two fields: type: (string | ReactClass) and props: Object
class Component {
constructor() {
this.state = 10;
this.setState = function() {
console.log('state');
};
}
handleClick() {
this.setState();
}
render() {
// return a child component.
return {
type: 'button',
props: {
// pass functional props
onClick: this.handleClick,
children: 'Click Me'
}
};
}
}
// 1. creating a component instance
const componentInstance = new Component();
// 2. calling a render method on the instance
// ( In reality, React does the same thing for your class components)
const element = componentInstance.render();
// 3. calling the onClick function, which was passed as a prop,
// will throw a 'TypeError: this.setState is not a function'.
element.props.onClick();
This TypeError is obvious now because this is pointing to the props object which doesn’t know the existence of any setState function. The setState function is only a property of componentInstance.
So, to fix this problem, we have to bind the handleClick function inside the constructor:
// inside constructor function
constructor() {
// bind returns a new function
this.handleClick = this.handleClick.bind(this);
}
...
// calling onClick will print 'state' this time.
element.props.onClick();
Now, the value of this will always point to componentInstance which has setState as one of its property and it will not throw any TypeError.
Now, that was the answer to our first question. It’s good progress so far. Moving forward, we shall try to find out the answer to our second question.
Looking at the code below:
let bar = { someMethod: function() { return this; } };
bar.someMethod(); // print {someMethod: f}
let foo = { someMethod: () => this};
foo.someMethod(); // prints global 'window' object
shows that arrow functions have no this of their own. It is always determined by the scope surrounding the arrow function when it was created.
Arrow functions have no this of their own.
When we use an arrow function inside our class (using property initializer feature), it becomes the method property of the instance. As this will always be determined by the outer scope, it will point to the instance of the class. Let’s see that in action:
class Component {
constructor() {
this.state = 10;
this.setState = function() {
console.log('state');
};
}
// using fat arrow function; no binding require inside constructor
handleClick = () => {
// this will now point to the instance of Component class which knows about the setState method property
this.setState();
};
render() {
// return a child component.
return {
type: 'button',
props: {
// pass functional props
onClick: this.handleClick,
children: 'Click Me'
}
};
}
}
// 1. creating a component instance
const componenttInstance = new Component();
// 2. calling a render method on the instance ( In reality, React does the same thing for your class components)
const element = componenttInstance.render();
// 3. calling onClick will now print 'state' to the console.
element.props.onClick();
I hope you have enjoyed reading this article and now will be able to answer the question confidently. I highly recommend reading this article by Dr. Axel Rauschmayer for a more detailed description of how this
works.
Thank you for reading, and if you liked the article a few ❤️ will definitely make me smile 😍.
Top comments (10)
Yep! This is the reason I always recommend arrow functions :)
this article actually helped me a lot, thank you very much <3 !
Thanks for your comment.
Thank you for this too good article! The explanation was smooth and the examples helped a lot! Almost all my doubts are clear.
Glad it was helpful.
Why React doesn't bind functions internally? It seems it's not that hard to implement this feature under the hood. Why do we need to mess around with bindings in the user land?
Well, seeing as this is javascript and not react, I would expect it not to. Classes as not unique to react so why should they work different in react from every other instance?
On top of that, doing any blackmagic auto bindings would more than likely end up with undesiderwd side effects which would result in "why IS react binding this internally?"
The idea behind react is to provide a highly performance dom manager while sticking as close to the core of javascript, you want black magic, that what angular is there for, to move as far away from the core language as possible.
Thanks, Reme for your reply 😀. And I think this is one of the reasons I always suggest my other fellow friends (who are new to React) to strengthen their JavaScript fundamentals first. Doing so will help them align and understand how any framework works.
Hum, that is strange. I mean their code structure is the reason for binding so why dont they just fix it and be done with it.
This was great! Thanks!