Creating functions inside the render method

Now, let's keep on adding features to List as we would do in a real-world application and see whether, at some point, we manage to break the benefits given by PureComponent.

For example, we want to add a click event handler on each item so that, if the item gets clicked, we log its value into the console.

This is far from being a useful feature in a real application, but you can easily figure out how, with a small amount of effort, you can create a more complex operation (for example, showing a new window with the item's data).

To add the logging feature, we have to apply two changes: one to the render method of  List and the other one to the render method of Item.

Let's start with the first one:

  render() { 
return (
<div>
<ul>
{this.state.items.map(item => (
<Item
key={item}
item={item}
onClick={() => console.log(item)}
/>
))}
</ul>
<button onClick={this.handleClick}>+</button>
</div>
);
}

We added an onClick prop to the Item component, and we set it to a function that, when called, logs the current item into the console.

To make it work, we have to add the logic on the Item components as well, and we can just use the onClick prop as the onClick handler of the <li> element:

  render() { 
return (
<li onClick={this.props.onClick}>
{this.props.item}
      </li> 
);
}

The component is still pure, and we expect it to not re-render if the values have not changed when baz is added to the list.

Unfortunately, if we run the component in the browser, we notice some new logs in DevTools. First of all, the whyDidYouUpdate library is telling us that there is a possibly avoidable re-render because the onClick function is always the same:

  Item.props.onClick
Value is a function. Possibly avoidable re-render?
before onClick() {
return console.log(item);
}
after onClick() {
return console.log(item);
}

Item.props.onClick
Value is a function. Possibly avoidable re-render?
before onClick() {
return console.log(item);
}
after onClick() {
return console.log(item);
}

The reason React thinks that we are passing a new function on every render is because the arrow function returns a newly created function every time it is invoked, even if the implementation remains the same. This is a pretty common mistake when using React, and it can be easily fixed by refactoring the components a bit.

Unfortunately, we cannot define the logging function once in the parent because we need to know which child has fired it; that is why creating it inside the loop seems the best solution.

What we actually have to do is move part of the logic inside the item that knows the item that has been clicked.

Let's see the full implementation of Item, which extends PureComponent:

  class Item extends PureComponent

It has constructor, where we bind the click handler, which is now part of its implementation:

  constructor(props) { 
super(props);

this.handleClick = this.handleClick.bind(this);
}

Inside the handleClick function, we call the onClick handler that we received from  props, passing the current item that has been clicked within the <li> element:

  handleClick() { 
this.props.onClick(this.props.item);
}

Finally, in the render method, we use the new local event handler:

  render() { 
return (
<li onClick={this.handleClick}>
{this.props.item}
</li>
);
}

The last thing to do is change the List component render to make it not return a new function at every single render, as follows:

  render() { 
return (
<div>
<ul>
{this.state.items.map(item => (
<Item key={item} item={item} onClick={console.log} />
))}
</ul>
<button onClick={this.handleClick}>+</button>
</div>
);
}

As you can see, we are passing the function we wanted to use, in this case console.log, which will be called inside the children with the right parameter. Doing so, the instance of the function in List is always the same and it does not fire any re-rendering.

If we now run the component in the browser and we click the + button to add the new item, which causes the List component to render, we can see that we are not wasting time anymore.

Also, if we click on any item in the list, we will see its value in the console as well.

As Dan Abramov says, it is not a problem by itself to use arrow functions inside the render method (or even to use bind to avoid binding in the constructor); we just have to be careful and make sure that it does not force any unnecessary re-rendering when PureComponent is in action.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.107.90