Why do we need to bind methods inside our component’s constructor?
December 12, 2018• ☕️ 3 min read
We all know that in order 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 that 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 JavaScript 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 a 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 function has no this of their own. It is always determined by the scope surrounding the arrow function when it was created.
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 happy coding 😃.
Written by Amandeep Singh. Developer @ Avarni Sydney. Tech enthusiast and a pragmatic programmer.