This post is from my personal Github
In normal development, there are always problems with handle binding. If you’re as lazy or thoughtful as I am, this process is pretty annoying. Record here my train of thought and course.
Here is an example of a button click event.
class App extends React.Components {
state = {
count: 0
}
clickHandler () {
const count = this.state.count + 1
this.setState({ count })
}
render() {
return (
<button>
Click me to show something in dev tools
</button>)}}Copy the code
The purpose of this example is to click the button to trigger the clickHandler to increment the counter by 1. We can fire the handle in two different ways, because we use this.setState, so we have to bind this to the function. Or use the arrow function to do this.
Directly in thejsx
insidebind(this)
<button onClick={this.clickHandler.bind(this)}>
Click me to show something in dev tools
</button>
Copy the code
Well, that can work. But it’s really long and it looks ugly. One problem is that the function is rebind every time it is re-rendered, which is not desirable for large lists.
Using the arrow function
Change clickHandler to the following paradigm.
clickHandler = () => {
const count = this.state.count + 1
this.setState({ count })
}
// render ...
<button onClick={this.clickHandler)}>
Click me to show something in dev tools
</button>
Copy the code
It would look so much better. But if you have OCD you will find one thing. If we add life cycle functions and some other handlers… Like this.
componentDidMount () {}
componentWillMount () {}
componentWillUpdate () {}
clickHandler = (a)= > {
const count = this.state.count + 1
this.setState({ count })
}
antoherHandle = (a)= > {}
Copy the code
You’ll notice that the life cycle function is written differently than the handler. But you can actually make them the same, for example, by changing the life cycle function to the arrow function. But doesn’t it look weird? After all, that’s not what you’ve been doing.
In addition, arrow functions cannot be inherited, which means that if your child component needs to inherit functions, it will not be able to do so. More important is the performance problem of not inheriting. This results in the overhead of creating new methods every time a component is created (because the implementation of the arrow function is actually throwing methods directly into the constructor function). If it is inherited, the methods will always come from the same prototype and the JS compiler will be optimized for that.
Arrow Functions in Class Properties Might Not Be As Great As We Think.
Used in the constructorbind(this)
Writing binding functions through constructors actually looks good
constructor (props) {
super(props)
this.clickHandler = this.clickHandler.bind(this)
this.antoherHandle = this.antoherHandle.bind(this)}Copy the code
Both solve the performance (memory) problem. You can do a lot of interesting things like take existing methods and add more semantic methods.
constructor (props) {
super(props)
this.clickHandler = this.clickHandler.bind(this)
this.antoherHandle = this.antoherHandle.bind(this)
this.clickWithOne = this.clickHandler.bind(this.1)}Copy the code
This produces a new event that passes the parameter 1 each time. It does look good. But there are still problems. As your method increases linearly, if you have three, four, five, and six, you may need to bind one by one. Add them to the constructor, or worse, by copying and pasting previously written methods, you’ll bind the wrong function. Just like that.
constructor (props) {
super(props)
this.clickHandler = this.clickHandler.bind(this)
this.antoherHandle = this.antoherHandle.bind(this)
this.clickWithOne = this.antoherHandle.bind(this.1)}Copy the code
You must know at runtime that your clickWithOne is bound to antoherHandle. If you haven’t tested it, there are likely to be problems or bugs that you don’t understand.
Automatic binding
If you think about it, you can write an autobind method from the binding function. But you were too lazy to write it, and you found a library called React-Autobind via Github. It looks good.
constructor(props) {
super(props);
autoBind(this);
}
Copy the code
You can even leave some methods unbound.
constructor(props) {
super(props);
autoBind(this, {
wontBind: ['leaveAlone1'.'leaveAlone2']}); }Copy the code
Or specify that only certain methods are bound.
constructor(props) {
super(props);
autoBind(this, {
bindOnly: ['myMethod1'.'myMethod2']}); }Copy the code
It looks wonderful. But you’ll notice that this is actually a pretty tedious way to write it. To write a bunch of stuff. Take a look at the source code and you’ll see that there’s a default wonBind list.
let wontBind = [
'constructor'.'render'.'componentWillMount'.'componentDidMount'.'componentWillReceiveProps'.'shouldComponentUpdate'.'componentWillUpdate'.'componentDidUpdate'.'componentWillUnmount'
];
Copy the code
The name of a function that does not require automatic binding. But this list is pretty bad, because as the React version progresses, some hooks and methods will be deprecated, and more methods may be added over time.
This library has not been updated for a long time. Give up on bad reviews…
Autobind-decorator
If you know ES7 decorators. You’ll find that the above is perfectly acceptable in decorator form, and that the library supports typescript as well. And the structure will be very clear. So you find the autobind-decorator library. It helps us, it gives us what we want, the documentation tells us at the beginning.
// Before:
<button onClick={ this.handleClick.bind(this) }></button>
// After:
<button onClick={ this.handleClick }></button>
Copy the code
With before… After use, it looks good and that’s what we want. One drawback of this library is that it must be supported at IE11+ or later, but that’s ok.
The other thing is that your support for starting decorators is in the Babel configuration.
{
"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true}}]]Copy the code
Let’s look at recommended uses.
import {boundMethod} from 'autobind-decorator'
class Component {
constructor(value) {
this.value = value
}
@boundMethod
method() {
return this.value
}
}
let component = new Component(42)
let method = component.method // .bind(component) isn't needed!
method() // returns 42
Copy the code
It makes more sense to bind this to the method instead of the entire class. Because not every method needs to use this. As Dan said.
It is unnecessary to do that to every function. This is just as bad as autobinding (on a class). You only need to bind OnClick ={this.dosomething}. Or fetch. Then (this.handledone) — Dan Abramov
You can use @autobind on functions as well as classes.
import autobind from 'autobind-decorator'
class Component {
constructor(value) {
this.value = value
}
@autobind
method() {
return this.value
}
}
let component = new Component(42)
let method = component.method // .bind(component) isn't needed!
method() // returns 42
// Also usable on the class to bind all methods
// Please see performance if you decide to autobind your class
@autobind
class Component {}Copy the code
It only applies to @boundClass, so we’re going to have to bind everything to this and it’s going to be much cleaner if we just boundClass.
import {boundClass} from 'autobind-decorator'
@boundClass
class Component {
constructor(value) {
this.value = value
}
method() {
return this.value
}
}
let component = new Component(42)
let method = component.method // .bind(component) isn't needed!
method() // returns 42
Copy the code
There are drawbacks as well, such as not being able to arbitrarily name different methods as constructor does and having to write a new one from the original method, but this is minor and harmless. And descorator has not become the standard, but in fact it is close enough, I don’t worry.
conclusion
All of the solutions here are different. I personally prefer autobind-decorator as one of the best solutions of all, but introducing an extra dependency is a bit of a hassle.