There are many situations when writing React where you’ll want to pass a function to a prop. Usually it’s to pass a callback down to a child component so that the child can notify the parent of some event.
It’s important to keep in mind the binding of the function – what its this
object will point to when it’s called.
There are a few ways to make sure the binding is correct, some better than others. This post will go over the options.
Way #1: Autobinding (good, only with React.createClass
)
If you’re using React.createClass
, the member functions in your component are automatically bound to the component instance. You can freely pass them around without calling bind
, and you’re always passing the same exact same function.
Way #2: Calling .bind Within render
(bad, ES6)
When using ES6 classes, React does not automatically bind the member functions inside the component.
Binding at the last second like this is one way to make it work correctly, but it will hurt performance slightly because a new function is being created every time it re-renders (which could be pretty often).
The trouble isn’t really that creating a function is an expensive operation. It’s that by creating a new function every time, the component you’re passing it to will see a new value for that prop every time. When it comes time to tune performance by implementing shouldComponentUpdate
, that constantly-changing prop will make it look like something changed when really it’s the same as before.
Here’s another variant that is doing the same thing, creating a function every time render
is called:
Way #3: Arrow Function in render
(bad, ES6)
Similar to the above example, except this one uses an arrow function instead of calling bind
. It looks nicer, but it still creates a function every time render
is called! No good.
Way #4: Property Initializers (good, ESnext)
This method works by setting handleClick
to an arrow function one time when the component is created. Inside render
and in other functions, this.handleClick
can be passed along without fear because the arrow function preserves the this
binding.
This one is labelled “ESnext” because it’s not technically part of ES6, ES7, or ES8. ES2016 and ES2017 have been finalized already, so if and when this makes it into the spec, it’ll likely be ES2018 or beyond.
Even though this is supported by Babel, there’s a (small) risk that this feature could be taken out of the spec and require some refactoring, but a lot of people are using it so it seems likely that it’ll stay put.
Way #5: Binding in the Constructor (good, ES6)
You can set up the bindings once in the constructor, and then use them forevermore! Just don’t forget to call super
.
Way #6: Using Decorators (good, ES8+)
There’s a nice library called autobind-decorator which makes it possible to do this:
The @autobind
decorator binds the handleClick
method and you’re all set. You can even use it on the entire class, if you’re lazy:
Once again, ES2016/ES7 doesn’t include this feature so you’re accepting a bit of risk by using it in your code, even though Babel does support it.
Bonus: Passing Arguments Without Bind
As Marc mentioned in the comments, it’s pretty common to use .bind
to preset the arguments for a function call, especially in lists, like this:
As explained here, one way to fix this and avoid the bind is to extract the <li>
into its own component that’ll call the click handler you pass in, with its id:
A Note on Performance
There’s a tradeoff with most of these methods: more (and more complex) code in exchange for some theoretical performance benefit.
“Premature optimization is the root of all evil,” said Donald Knuth. So… before you split up or complicate your code to save a few cycles, actually measure the impact: pop open the dev tools and profile the code and use the React performance tools.
Wrap Up
That about covers the ways to bind the functions you’re passing to props. Know of any other ways? Got a favorite one? Let us know in the comments.