Do I still need to bind React functions in 2019?
One of Reddit members asked me a question in follow-up to my answer to a question about this
keyword:
Why do I need to bind a function if it’s in a class?
Short answer
You don’t need to use bind
. If you use the following syntax to declare class methods, then this
in them will be automatically bound to the current class instance. It’s magic.
class MyComponent extends React.Component {
myMethod1 = () => {
...
}
myMethod2 = () => {
...
}
}
Note the absence of const
keyword.
That syntax was introduced in ES6 class field declarations proposal and already widely supported. That is what you should do when you write React class-based components in 2019. bind
is obsolete.
Long answer
When JavaScript was created, it wasn’t an object-oriented language. It was a quirky object-oriented-wannabe, which in many cases behaved like one but not quite. One of those quirks was its this
keyword.
Object-oriented languages like Java and C++ have this
keyword too. In those languages, it always points to the current instance of an object type (aka class).
JavaScript wasn’t like that. In it, this
could be pointing to the current class instance (as you would expect), but also to the browser window object, to the current function, and it could be just null
. There were rules behind that behaviour, but they were so complex nobody could understand them. If you curious, check out any of these articles:
- Binding functions in React
- React — to Bind or Not to Bind
- This is why we need to bind event handlers in Class Components in React
Why was that important? Let’s look at an example.
class MyComponent extends React.Component {
state = {
text: "abc"
};
setText {
this.setState({ text: "123" }); // What is "this" here???
}
render() {
return (
<div>
<div>{this.state.text}</div>
<button onClick={this.setText}>Click me</button>
</div>
);
}
}
The code above doesn’t work.
This is what was supposed to happen here:
- We click on “Click me” button, and
setText
method is executed because it is bound to the button’sonClick
handler. setText
method sets the component’s statetext
value to “123” by calling React’ssetState
method.
However, what we really get here is an obscure “A cross-origin error was thrown” error when we click the button.
Why?
Because in order for this.setState
to work, this
inside setText
method must be pointing to the current instance of MyComponent
class. But it doesn’t. It is pointing to something else entirely. Yes, it is possible to decipher the complex rules governing the value of this
in that case and tell why. But I wouldn’t bother. It’s just too hard.
However, it is much easier to avoid that problem entirely.
There are 3 ways to solve it:
- Explicitly bind the value of
this
insidesetText
to the current class instance. That can be done in a constructor:
class MyComponent extends React.Component {
...
constructor(props) {
super(props);
this.setText = this.setText.bind(this);
}
...
}
- Use an arrow function in an event handler. This magically makes
this
in the function it calls bound to the current class instance too:
<button onClick={() => this.setText()}>Click me</button>
- You already know it. Use class field syntax for method declaration:
class MyComponent extends React.Component {
...
setText = () => {
this.setState({ text: "123" });
}
...
}
As of 2019, this method is considered superior. Facebook has been using it internally since 2017:
Or, even better, drop class-based components altogether and switch to functional components with hooks.
Happy coding!