I\'m running lint with my React app, and I receive this error:
error JSX props should not use arrow functions react/jsx-no-bind
And this is where I\'m running the arrow function (inside onClick
):
{this.state.photos.map(tile => (
<span key={tile.img}>
<Checkbox
defaultChecked={tile.checked}
onCheck={() => this.selectPicture(tile)}
style={{position: \'absolute\', zIndex: 99, padding: 5, backgroundColor: \'rgba(255, 255, 255, 0.72)\'}}
/>
<GridTile
title={tile.title}
subtitle={<span>by <b>{tile.author}</b></span>}
actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color=\"white\"/></IconButton>}
>
<img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: \'pointer\'}}/>
</GridTile>
</span>
))}
Is this a bad practice that should be avoided? And what\'s the best way to do it?
Why you shouldn\'t use inline arrow functions in JSX props
Using arrow functions or binding in JSX is a bad practice that hurts performance, because the function is recreated on each render.
Whenever a function is created, the previous function is garbage collected. Rerendering many elements might create jank in animations.
Using an inline arrow function will cause PureComponent
s, and components that use shallowCompare
in the shouldComponentUpdate
method to rerender anyway. Since the arrow function prop is recreated each time, the shallow compare will identify it as a change to a prop, and the component will rerender.
As you can see in the following 2 examples - when we use inline arrow function, the <Button>
component is rerendered each time (the console shows the \'render button\' text).
Example 1 - PureComponent without inline handler
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log(\'render button\');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
onClick = () => this.setState((prevState) => ({
counter: prevState.counter + 1
}));
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ this.onClick } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById(\'root\')
);
<script crossorigin src=\"https://unpkg.com/react@16/umd/react.production.min.js\"></script>
<script crossorigin src=\"https://unpkg.com/react-dom@16/umd/react-dom.production.min.js\"></script>
<div id=\"root\"></div>
Example 2 - PureComponent with inline handler
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log(\'render button\');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ () => this.setState((prevState) => ({
counter: prevState.counter + 1
})) } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById(\'root\')
);
<script crossorigin src=\"https://unpkg.com/react@16/umd/react.production.min.js\"></script>
<script crossorigin src=\"https://unpkg.com/react-dom@16/umd/react-dom.production.min.js\"></script>
<div id=\"root\"></div>
Binding methods to this
without inlining arrow functions
Binding the method manually in the constructor:
class Button extends React.Component {
constructor(props, context) {
super(props, context);
this.cb = this.cb.bind(this);
}
cb() {
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
Binding a method using the proposal-class-fields with an arrow function. As this is a stage 3 proposal, you\'ll need to add the Stage 3 preset or the Class properties transform to your babel configuration.
class Button extends React.Component {
cb = () => { // the class property is initialized with an arrow function that binds this to the class
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
This is because an arrow function apparently will create a new instance of the function on each render if used in a JSX property. This might create a huge strain on the garbage collector and will also hinder the browser from optimizing any \"hot paths\" since functions will be thrown away instead of reused.
You can see the whole explanation and some more info at https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
To avoid creating new functions with the same arguments, you could memoize the function bind result, here is a simple utility named memobind
to do it: https://github.com/supnate/memobind