How to avoid bind or inline arrow functions inside render method

Viewed 16625

We should avoid method binding inside render because during re-rendering it will create the new methods instead of using the old one, that will affect the performance.

So for the scenarios like this:

<input onChange = { this._handleChange.bind(this) } ...../>

We can bind _handleChange method either in constructor:

this._handleChange = this._handleChange.bind(this);

Or we can use property initializer syntax:

_handleChange = () => {....}

Now lets consider the case where we want to pass some extra parameter, lets say in a simple todo app, onclick of item i need to delete the item from array, for that i need to pass either the item index or the todo name in each onClick method:

todos.map(el => <div key={el} onClick={this._deleteTodo.bind(this, el)}> {el} </div>)

For now just assume that todo names are unique.

As per DOC:

The problem with this syntax is that a different callback is created each time the component renders.

Question:

How to avoid this way of binding inside render method or what are the alternatives of this?

Kindly provide any reference or example, thanks.

4 Answers

This answer https://stackoverflow.com/a/45053753/2808062 is definitely exhaustive, but I'd say fighting excessive re-renders instead of just re-creating the tiny callback would bring you more performance improvements. That's normally achieved by implementing a proper shouldComponentUpdate in the child component.

Even if the props are exactly the same, the following code will still re-render children unless they prevent it in their own shouldComponentUpdate (they might inherit it from PureComponent):

handleChildClick = itemId => {}

render() {
    return this.props.array.map(itemData => <Child onClick={this.handleChildClick} data={itemData})
}

Proof: https://jsfiddle.net/69z2wepo/92281/.

So, in order to avoid re-renders, the child component has to implement shouldComponentUpdate anyway. Now, the only reasonable implementation is completely ignoring onClick regardless of whether it has changed:

shouldComponentUpdate(nextProps) {
    return this.props.array !== nextProps.array;
}

Documentation encourages to use data-attributes and access them from within evt.target.dataset:

_deleteTodo = (evt) => {
  const elementToDelete = evt.target.dataset.el;
  this.setState(prevState => ({
    todos: prevState.todos.filter(el => el !== elementToDelete)
  }))
}

// and from render:

todos.map(
  el => <div key={el} data-el={el} onClick={this._deleteTodo}> {el} </div>
)

Also note that this makes sense only when you have performance issues:

Is it OK to use arrow functions in render methods?

Generally speaking, yes, it is OK, and it is often the easiest way to pass parameters to callback functions.

If you do have performance issues, by all means, optimize!

Related