You’re right, there are a bunch of ways to handle handlers (ba-dum-tss). React has been around for a while now and the face of JavaScript has changed quite a bit in that time.
There’s a whole page on handling events in the React documentation, but nevertheless here’s a comparison of some of the ways callbacks are handled:
class MyComponent extends React.Component {
constructor (props) {
super(props)
this.boundHandleClick = this.boundHandleClick.bind(this)
}
arrowHandleClick = (event) => { this.props.onClick(event.target.id) }
boundHandleClick (event) { this.props.onClick(event.target.id) }
boundInRenderHandleClick (event) { this.props.onClick(event.target.id) }
unboundHandleClick (event) { this.props.onClick(event.target.id) }
render () {
return (
<div>
<button id='zero' onClick={(event) => { this.props.onClick(event.target.id) }} />
<button id='one' onClick={arrowHandleClick} />
<button id='two' onClick={boundHandleClick} />
<button id='three' onClick={boundInRenderHandleClick.bind(this)} />
<button id='four' onClick={unboundHandleClick} />
</div>
)
}
}
When clicked:
#zero
will correctly call props.onClick
. The problem with this is that the anonymous function created in the render method will be recreated on every render. This is not good for performance.
#one
will correctly call props.onClick
. Because the callback is defined as a class method, it will only be created when MyComponent
is instantiated (and mounted). This is an acceptable way to define a callback. Apparently it’s slower than once thought, but it’s also the neatest in my opinion.
#two
will correctly call props.onClick
. This is essentially the same as arrowHandleClick
, only it’s a bound function as opposed to an arrow function. For all intents and purposes, they’re the same - but by all means dig into the differences.
#three
will correctly call props.onClick
, and it has the same result as #two
, but has the negative performance impact as #zero
- functions should not be created nor bound in the render method.
#four
will not work correctly and will throw an error. When it’s run, this
will be referring to the element (in this case, #four
) rather than the class instance. In all other handlers, this
refers to the React class instance, which has a props.onClick
.
Now there is a new accepted way of writing components: using plain functions and hooks. this
is a thing of the past.
const MyComponent = (props) => {
const handleClick = event => props.handleClick(event.target.id)
const memoizedHandleClick = React.useCallback(
event => props.handleClick(event.target.id),
[props.handleClick]
)
return (
<div>
<button id='zero' onClick={event => props.handleClick(event.target.id)} />
<button id='one' onClick={handleClick} />
<button id='two' onClick={memoizedHandleClick} />
</div>
)
}
All callbacks work correctly here - the only difference is that memoizedHandleClick
will not be recreated on every render, unless props.handleClick
changes. In my own experience, both are acceptable and people don’t really seem to mind about recreating callbacks in functional components anymore - but rather being pragmatic and addressing performance issues when you encounter them.