How to avoid unnecessary re-renders in the react component
React has come a long way from its early stages. Still, many of us find it difficult to fix the un-necessary re-renders.
There are many approaches out there to avoid re-renders. Here I am discussing some of the methods and approaches to avoid unnecessary re-renders in React.
Replacing
useState()
withuseRef()
hook.Using the Reselect library to create Memoized selectors
Using SWR a React Hooks library for data fetching.
Memoization using
useMemo()
anduseCallback()
Hooks.
1. Replacing useState() with useRef() hook.
useState()
is is commonly used hook in React functional components to re-render the component on state changes. But in some cases, we need to track the update without re-rendering the component. To solve this we can use useRef()
hook, when we use useRef() for the updates it will not fire re-rendering unlike useState()
Example with useState():
function inputWithState() {
const [value, setValue] = useState("");
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
In this example every keystroke in the input will re-render because of the state change with the onChange event.
Example with useRef():
function inputWithRef() {
const inputEl = useRef(null);
console.log(inputEl?.current?.value); return (
<input ref={inputEl} type="text" />
);
}
In this example whatever you type in input you can read that using input reference. This approach avoids unnecessary re-rendering on each keystroke.
2. Using the Reselect library to create Memoized selectors
React components have a fast lifecycle, they always suffer due to so much re-rendering, causing production time and performance. To fight this, developers created a third-party performance library re-select, a wrapper for the popular library Reselect, which is used with Redux to improve the performance by coding memoized selectors.
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 composable. They can be used as input to other selectors.
Example:
import { createSelector } from 'reselect'
const selectShopItems = state => state.shop.items;
const selectTaxPercent = state => state.shop.taxPercentconst selectSubtotal = createSelector(selectShopItems, items =>
items.reduce((subtotal, item) => subtotal + item.value, 0)
);
const selectTax = createSelector(
selectSubtotal,
selectTaxPercent,
(subtotal, taxPercent) => subtotal * (taxPercent / 100)
);
const selectTotal = createSelector(
selectSubtotal,
selectTax,
(subtotal, tax) => ({ total: subtotal + tax })
);
const exampleState = {
shop: {
taxPercent: 8,
items: [
{ name: 'apple', value: 1.2 },
{ name: 'orange', value: 0.95 }
]
}
}
console.log(selectSubtotal(exampleState)) // 2.15
console.log(selectTax(exampleState)) // 0.172
console.log(selectTotal(exampleState)) // { total: 2.322 }
Here, the createSelector
takes 2 selectors as the input and returns the memoized version. Selectors will not be re-computed again with this memoized version until the values are different.
While Reselect is not exclusive to Redux, it is already included by default in the official Redux Toolkit package — no further installation is needed.
3. Using SWR a React Hooks library for data fetching
SWR is a React Hooks library for data fetching.
The name “SWR” is derived from stale-while-revalidate
. SWR first returns the data from the cache (stale), then sends the request (revalidate), and finally comes with the up-to-date data again. It will prevent the component from re-rendering multiple times.
With just one hook, you can significantly simplify the data-fetching logic in your project. And it also covers all aspects of speed, correctness, and stability to help you build better.
Example:
function Profile() {
const { data, error } = useSWR('/api/user', fetcher)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
Refer here for more info and examples: https://swr.vercel.app/
4. Memoization using useMemo()
and useCallback()
Hooks
Memoization enables our react code to re-render components only if there is an update in the props. With this approach, developers can avoid unnecessary re-renderings and reduce the computational load in applications.
React provides two Hooks to create memoization:
useMemo()
useCallback()
These Hooks reduce re-renderings by caching and returning the same output if the inputs are the same without any computations. When the inputs update, the cache gets invalidated and the new component state gets rendered.
- useMemo()
This hook is used to memoize a calculation result between a function’s calls and between renders.
Example:
const expensiveFunction = (inputValue) => {
let expensiveValue = inputValue * 42;
//... lots and lots of computing including inputValue ...
expensiveValue = 'World';
return expensiveValue;
};
const MyComponent = ({ something }) => {
const [inputValue, setInputValue] = useState('');
const expensiveValue = useMemo(
() => expensiveFunction(inputValue),
[ inputValue ]
);
return <h1>Hello {expensiveValue}</h1>;
};
2. useCallback()
This is another React Hook to implement memoization. But, unlike useMemo()
, it does not cache the result. Instead, it memoizes the callback function provided to it.
Example:
function item() {
const onClick = useCallback(event => {
console.log('Clicked Item : ', event.currentTarget);
}, [item]); return (
<li={item} onClick={onClick} />
);
}
In example, useCallBack()
memoizes the onClick
callback. So, it will not re-render the component if the user clicks the same item again and again.
Hope this article helps developers to solve re-rendering issues in React Components. Please help to learn if you have any other approach or techniques to avoid re-rendering in React please add it in the comments.
Thanks for the read. Cheers!!!.
You are Awesome!
Follow me on twitter/X
Follow me on linkedIn
Check out My portfolio