This blog post offers 10 practical tips and techniques to help developers build high-performing React applications. From optimising component rendering to managing state and data fetching, readers will learn best practices that can help their applications run faster and smoother, ultimately improving the user experience.
Table of contents
Open Table of contents
Using Functional Components and Hooks Instead of Classes
React supports building components in on of two ways. Using classes by inheriting from React.Component or by writing pure functions that return an JSX object.
What you get by using functional components?
Readability, better testing, much less code and no boilerplate π
Lets have a look at a simple example:
class NasaData extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
fetch("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
.then(res => res.json())
.then(json => {
this.setState({
data: json,
});
});
}
render() {
const { data } = this.state;
if (!data.length)
return (
<div>
ββββββββββ<h1> Fetching data.... </h1> ββββββββ
</div>
);
return (
<>
ββββββββ<h1> Fetch data using Class component </h1> ββββββββ
{data.map(item => (
<div key={item.id}>{item.title}</div>
))}
ββββββ
</>
);
}
}
The same thing using a function and hooks:
const NasaData = () => {
const [data, setdata] = useState(null);
useEffect(() => {
fetch("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
.then(res => res.json())
.then(json => {
setdata(json);
});
}, [data]);
return (
<>
ββββββ<h1> Fetch data using Class component </h1> ββββββ
{data.map(item => (
<div key={item.id}>{item.title}</div>
))}
ββββ
</>
);
};
Even though the above block of code does the same thing as the class component, it is less complex, minimal, and easy to understand.
Avoid Using State
React state keeps track of the data which when changed triggers the React component to re-render. When building React applications, avoid using state as much as possible since the more state you use, the more data you have to keep track of across your app.
One way of minimizing the use of state is by declaring it only when necessary. For instance, if you are fetching user data from an API, store the whole user object in the state instead of storing the individual properties.
Instead of doing this:
const [username, setusername] = useState("");
const [password, setpassword] = useState("");
Do this:
const [user, setuser] = useState({});
Avoid Using Indexes as Key Props
React uses keys to uniquely identify items in an array. With keys, React can pinpoint which item has been changed, added, or removed from the array.
When rendering arrays, you might use the index as the key.
const Items = () => {
const arr = ["item1", "item2", "item3", "item4", "item5"];
return (
<>
ββββββ
{arr.map((elem, index) => {
<li key={index}>{elem}</li>;
})}
ββββ
</>
);
};
While this sometimes works, using the index as a key can introduce issues especially if the list is expected to change. Consider this list.
const arr = ["item1", "item2", "item3", "item4", "item5"];
Currently, the first list item, βItem1β is at index zero, but if you added another item at the beginning of the list, the βItem1β index would change to 1 which changes the behavior of your array.
The solution is to use a unique value as the index to ensure that the identity of the list item is maintained.
Opt for Fragments Instead of Divs Where Possible
React components need to return code wrapped in a single tag usually a <div>
or a React fragment. You should opt for fragments where possible.
Using <div>
increases the DOM size, especially in huge projects since the more tags or DOM nodes you have, the more memory your website needs and the more power a browser uses to load your website. This leads to lower page speed and potentially poor user experience.
One example of eliminating unnecessary <div>
tags is not using them when returning a single element.
const Button = () => {
return <button>Display</button>;
};
Avoid Repetitive Code (DRY principle)
If you notice you are writing duplicated code, convert it into components that can be reused.
For example, it makes more sense to create a component for your navigation menu instead of repeatedly writing the code in every component that requires a menu.
Thatβs the advantage of a component-based architecture. You can break down your project into small components you can reuse across your application.
Use Object Destructuring For Props
Instead of passing the props object, use object destructuring to pass the prop name. This discards the need to refer to the props object each time you need to use it.
For example, the following is a component that uses props as is.
const Button = props => {
return <button>{props.text}</button>;
};
With object destructuring, you refer to the text directly.
const Button = ({ text }) => {
return <button>{text}</button>;
};
Dynamically Render Arrays Using Map
Use map() to dynamically render repeated HTML blocks. For example, you can use map() to render a list of items in <li>
tags.
const Items = () => {
const arr = ["item1", "item2", "item3", "item4", "item5"];
return (
<>
ββββββ
{arr.map((elem, index) => {
<li key={elem + index}>{elem}</li>;
})}
ββββ
</>
);
};
For comparison purposes, here is how you can render the list without map(). This approach is very repetitive.
const List = () => {
return (
<ul>
ββββββ<li>Item1</li>
ββββββ<li>Item2</li>
ββββββ<li>Item3</li>
ββββββ<li>Item4</li>
ββββββ<li>Item5</li>
ββββ
</ul>
);
};
Write tests π₯Έ
Although React is very flexible in how you can use it, following specific practices will help you get the most out of your experience.
When following these tips, keep your particular project and goals in mind as different React best practices may be more relevant in different contexts. For instance, a project with a small team and a limited scope may not require the same level of folder organisation as a large project with multiple teams working together.
React testing libraries
There are several popular testing libraries and frameworks available for testing React applications. Here are some of the most commonly used ones:
- Jest: Jest is a popular testing framework that is widely used for testing React applications. It comes with built-in support for React and provides features like snapshot testing and code coverage reporting.
- Enzyme: Enzyme is a JavaScript testing utility for React that makes it easier to test React components. It provides a set of APIs for testing componentsβ output, behavior, and state.
- React Testing Library: The React Testing Library is a lightweight and simple testing library that provides utilities for testing React components in a way that simulates user behavior.
- Cypress: Cypress is a powerful end-to-end testing framework that can be used for testing React applications. It allows you to write tests that simulate user interactions and can be used to test your applicationβs functionality, performance, and accessibility.
- Testing Frameworks: In addition to the libraries above, there are several popular testing frameworks that can be used for testing React applications, including Mocha, Chai, and Jasmine.
Overall, the choice of testing library or framework will depend on your specific testing needs and preferences.
Final clump of advice
- Optimize Component Rendering: Avoid unnecessary rendering by using shouldComponentUpdate, PureComponent, or React.memo. Avoid binding functions in the render method.
- Use State and Props Wisely: Avoid storing unnecessary data in state or props, and make sure to update them efficiently. Pass data between components only when necessary.
- Minimize the Use of HOCs: Higher-Order Components (HOCs) can reduce the performance of React applications. Use them judiciously and avoid nesting them.
- Implement Code Splitting: Break down large application bundles into smaller, more manageable pieces. Load only the necessary code for each page or component.
- Use React Lazy and Suspense: Load components only when necessary. Use React.lazy() and Suspense to render components only when they are required.
- Optimize Images and Other Assets: Optimize images and other assets to minimize their size and loading time. Use responsive images and compress them using tools like Webpack or Gulp.
- Use Server-Side Rendering (SSR): Use SSR to render components on the server and send them to the client as HTML. This can significantly reduce the time it takes to render the initial view.
- Use a State Management Library: Use a state management library like Redux or MobX to manage complex state and data flows. These libraries provide efficient ways to update and access data.
- Use React Profiler: Use the React Profiler tool to identify performance bottlenecks in your application. It can help you optimize rendering and data fetching.
- Implement Code Reviews: Conduct regular code reviews to identify performance issues and ensure that best practices are followed. This can help catch issues early and improve the overall performance of your application.