To read more articles like this, visit my blog
ReactJS is very performant by default. But now and then, you get a chance to make it even better. And the incredible React community has come up with some fantastic libraries.
Today we will talk about seven such libraries that can improve the quality of your code and, at the same time, improve performance.
Let’s begin.
1. React Query
React Query is known to be the missing state management library for React. In its documentation, it says, “Fetch, cache, and update data in your React applications all without touching any ‘global state’.”
Yes. This is precisely what it does. It helps us to manage the server state without any hassle. It can reduce the necessity for using a state management library like Redux.
Advantages
Automatic caching
Updates data automatically in the background
Reduces code by a lot
Before React Query
Here is an example hook to fetch data using our own custom hook. It doesn't even support caching.
const useFetch = (url) => {
const [data, setData] = useState();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await fetch(url);
setData(result.data);
} catch (error) {
setError(error);
}
setIsLoading(false);
};
fetchData();
}, [url]);
return {data , isLoading , isError}
}
After React Query
Here is the code if we want to use React Query. Look how small it is.
import { useQuery } from 'react-query'
const { isLoading, error, data } = useQuery('repoData', () =>
fetch(url).then(res =>res.json()
)
)
Look how much it reduced our code.
2. React Hook Form
React Hook Form is the modern form-handling library that can take your form's performance to a new level.
Advantages
Reduces code
Reduces unnecessary re-rendering
Easily integrates with modern UI libraries
Following is an example demonstrating how React Hook Form can improve code quality.
Without React Hook Form
Here is an example of building a login form manually.
function LoginForm() {
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log({email, password});
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</form>
);
}
With React Form
Here is the same example with React Hook Form.
function LoginForm() {
const { register, handleSubmit } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email")} />
<input {...register("password")} />
<input type="submit" />
</form>
);
}
It’s so clean and, at the same time, performant. Give it a try.
3. React Window
React Window is used to render long lists. Imagine you have a list of 1,000 items. Only ten are visible simultaneously, but your code tries to render all 1,000 items simultaneously.
This can cause serious studdering in your application. This is a very popular library and a must-have tool in your arsenal.
Manual rendering of 1,000 items
import React, {useEffect, useState} from 'react';
const names = [] // 1000 names
export const LongList = () => {
return <div>
{names.map(name => <div> Name is: {name} </div>)}
<div/>
}
But this code renders 1,000 items simultaneously, although you can see at most ten–20 items on your screen.
Using React Window
Now let’s use React Window.
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => <div style={style}> Name is {names[index]}</div>
const LongList = () => (
<List
height={150}
itemCount={1000}
itemSize={35}
width={300}
>
{Row}
</List>
);
This code renders only what you see on the screen. It can be intimidating initially but necessary if you have a long list to render.
4. React LazyLoad
Lazy loading is a technique used to load only what you need. Thus it improves performance by not overusing computation power.
React LazyLoad is a library specifically built for that purpose. You just wrap your component, and this library takes care of the rest.
Advantages
Improved performance
Supports server-side rendering
Without LazyLoad
Here is an example where we are loading five images manually.
import React from 'react';
const ImageList = () => {
return <div>
<img src ='image1.png' />
<img src ='image2.png' />
<img src ='image3.png' />
<img src ='image4.png' />
<img src ='image5.png' />
</div>
}
With LazyLoad
Here is the same example with the LazyLoad component.
import React from 'react';
import LazyLoad from 'react-lazyload';
const ImageList = () => {
return <div>
<LazyLoad> <img src ='image1.png' /> <LazyLoad>
<LazyLoad> <img src ='image2.png' /> <LazyLoad>
<LazyLoad> <img src ='image3.png' /> <LazyLoad>
<LazyLoad> <img src ='image4.png' /> <LazyLoad>
<LazyLoad> <img src ='image5.png' /> <LazyLoad>
</div>
}
5. Why Did You Render
Unnecessary rendering can hurt the performance of your React applications performance. But sometimes, we do it without even knowing.
This awesome package, Why Did You Render, helps us to find performance issues and solve them. You just turn it on in any component, and it tells you exactly why it renders.
Following is a component with render issues.
import React, {useState} from 'react'
const WhyDidYouRenderDemo = () => {
console.log('render')
const [user , setUser] = useState({})
const updateUser = () => setUser({name: 'faisal'})
return <>
<div > User is : {user.name}</div>
<button onClick={updateUser}> Update </button>
</>
}
export default WhyDidYouRenderDemo
Once turned on, this library will console-log the following output.
This log shows that we are updating the object with the same value, which is terrible for performance.
6. Reselect
If you are using Redux, then this is a lifesaver. We know Redux reducers can store a lot of data, and if you feed the complete store into any component, it will cause it to re-render anytime anything in that store updates.
Reselect solves this problem by memorizing the values and only passing what’s necessary.
Advantages (from the documentation)
Selectors can compute derived data, allowing Redux to store the minimal possible state.
Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
Selectors are compostable. They can be used as input to other selectors.
Example
Following is an example of bringing the values from inside the store and modifying them in a selector.
import { createSelector } from 'reselect'
const shopItemsSelector = state => state.shop.items
const subtotalSelector = createSelector(
shopItemsSelector,
items => items.reduce((subtotal, item) => subtotal + item.value, 0)
)
const exampleState = {
shop: {
items: [
{ name: 'apple', value: 1.20 },
{ name: 'orange', value: 0.95 },
]
}
}
7. Deep Equal
Deep Equal is a famous library that can be used to compare. This is handy. After all, in JavaScript, although two objects can have the same values, they are considered different because they point to different memory locations.
That’s why we see the following kind of result.
const user1 = {
name:'faisal'
}
const user2 ={
name:'faisal'
}
const normalEqual = user1 === user2 // false
But if you need to check for equality (for memoization), it becomes a costly (and complex) operation.
If we use Deep Equal, then it improves performance by** 46 times. **Below is an example of how we can use it.
var equal = require('deep-equal');
const user1 = {
name:'faisal'
}
const user2 ={
name:'faisal'
}
const deepEqual = equal(user1 , user2); // true -> exactly what we wanted!
There you go. These are some of the most important libraries you can use to maximize the performance of your React application.
Leave a comment if you have some other ones in mind. Have a great day!
Have something to say? Get in touch with me via LinkedIn or Personal Website