React

React: Best Practices to follow for Better Performance

Eyas Mattar
4 min readNov 24, 2019

As the need for better performance has emerged at Facebook, Reactjs were developed. It has been maintained over the time by the open-source Reactjs Community.

The fact that React performs a “Diffing Algorithm” or “Reconciliation Algorithm” to determine what portions of the DOM elements in the tree to update, makes it a good choice for building performance-optimized applications.

To help reacts’ algorithm make good decisions, we should take into account some few practices.

Mutablility is the most important thing in react so we can detect props or state changes. However, we should avoid some bad practices that overuse mutablility and thus causes unwanted and redundant changes to the components’ state and to the components’ props that leads to poor component performance.

1. Defining functions in render() (AVOID)

render() {
const onChange = event => showMessage(event);

return (
<Wrapper>
<Form onChange={onChange} {...props} />
<Fill {...props} />
</Wrapper>
);
}

On every update of this component we are creating a new COPY of onChange function so the Children will always renew their props and thus they will always render whenever the parent renders.

To resolve this issue, just remove the function to the Global scope so the onChange function will be declared only once.

2. Calculating static props on the render() function (AVOID)

render() {
const metaData = {
colors: getColors(),
title: getTitle(),
}
return (
<Wrapper>
<Form onChange={onChange} {...props} />
<Fill data={metaData} />
</Wrapper>
);
}

Static props are props that we know that they will not change through Components’ lifecycle and thus we should calculate them only once.

If we calculate them in the render() function we will be failing for two reasons:

  • We do the same redundant logic at every component update
  • We are creating new Object every component update, and thus when we pass it to the children, children obviously will do useless and unwanted re-renders. Furthermore, if we have this same practice in all children we will be rendering the whole tree unnecessarily.

3. Computed Selectors & Reselect

const mapStateToProps = state => ({
users: getUsersWithReviews(state),
meta: getMetaData(state),
todos: getTodos(state),
});
const getUsersWithReviews = (state) => {
// some complicated logic
}
const getMetaData= (state) => {
// some complicated logic
}

Let’s say we want to connect our component to the state and get some calculated objects- users, metadata, and todos.

We would like to execute this logic ONLY when the related data changes in the state, so our Container will be more efficient.

Example:

const getUsersWithReviews = (state) => {
const users = getUsers(state);
const reviews = getReviews(state);
// some complicated logic.....
}

In order to calculate our data, we fetch users & reviews, so we will be needing only this portion of the state.

In an ideal application with performance optimization, whenever changes made on other portions of the state our calculated data stays the same so we don’t need to re-calculate this data yet.

We can achieve this by integrating reselect library https://github.com/reduxjs/reselect

Example:

export const getUsersWithReviews = createSelector([getUsers, getReviews], (users, reviews) => {    
// some complicated logic.....
});

createSelector is responsible for executing the callback we provided only when the provided selectors changes.

4. Class vs. Function

Let’s examine two differences

Function

const FormA = () => {

const onChange = () => {
// some logic
};

const onClick = () => {
// some logic
};

return (
<div>
<Input type="text" onChange={onChange} />
<Input type="number" onChange={onChange} />
<Input type="number" onChange={onChange} />
....
<Button type="number" onClick={onClick} />
</div>
);
};

Class

class FormB extends React.Component {

onChange = () => {
// some logic
};

onClick = () => {
// some logic
};

render() {
return (
<div>
<Input type="text" onChange={this.onChange}/>
<Input type="number" onChange={this.onChange}/>
<Input type="number" onChange={this.onChange}/>
....
<Button type="number" onClick={this.onClick}/>
</div>
);
}
}

On every FormA re-render, all his children will also re-render.

On every FormB re-render, NONE of his children will re-render.

Conceptually these two components are the same, but in terms of mutability they are very different.

On every FormA re-render, we are creating NEW onChange and NEW onClick.

On every FormB re-render, we are passing the same onChange onClick.

We can argue that FormB has Lifecycle functions, and FormA has none, but in matters of re-rendering FormB is the optimized option.

5. Keys

Reconciliation Algorithm recurses over the children of the current tree node to check in order to compare changes. But the algorithm assumes that the order of the children didn’t change, so whenever we add a child to the begging of the childrens’ array, all children will re-render.

In order to help react determine what actually changed, we need to provide a key attribute to every child to uniquely identify the child element so when the algorithm compare elements, it can identify the same elements in the two different trees and not count on the unreserved order of the elements.

class FormB extends React.Component {

getChildren = () => {
const { products } = this.props;
const productsElements = [];
if (products) {
products.map(product =>
productsElements.push(
<div key={product.id}>{product.name}</div>));
}
return productsElements;
};

render() {
return (
<div>
{this.getChildren()}
</div>
);
}
}

--

--

No responses yet