Are you struggling to make your gigantic react app more efficient in terms of Performance? โก
Don't worry! You've landed at the right location. These 5 methods will make your slow ๐ข React app to a fast app ๐.
For busy readers, here are the 5 methods in a nutshell.
useCallback
useMemo
memoized components
Components Lazy import
Image Lazy loading
1. useCallback
When you declare a normal component, it runs each time when your component renders.
Instead of declaring a normal function in the component, wrap it with useCallback.
useCallback is a React hook that gives you a cached function. It is cached until any of its dependencies are changed.
This way you get 2 benefits. 1) On change of any of its dependencies you get a fresh function respective to that value. 2) Unnecessary function calls are reduced which in turn increases the app's performance.
Have a look at this basic conversion from normal function to useCallback function.
export const MyProfile: React.FC = (props) => {
const {birthYear, name} = props
// normal function. this is called everytime component renders
const calculateAge = () => {
const currentYear = new Date().getFullYear()
return currentYear - birthYear
}
// efficient function. this will be called only when dependencies will be changed
const efficientCalculateAge = useCallback(() => {
const currentYear = new Date().getFullYear()
return currentYear - birthYear
}, [birthYear])
return (
<Fragment>
<p>Your Age : {efficientCalculateAge()}</p>
</Fragment>
)
}
2. useMemo
Typically in a React component, there are more variables than functions.
Above we mentioned to cache functions. How if we can cache variables also? ๐ค
Here useMemo enters your game. Just do all the computation stuff and store the value wrapped with useMemo.
Benefits of useMemo are the same as useCallback. Just the difference would be useMemo will return a variable while useCallback will return a function.
Here is how you can simply implement useMemo in your project.
export const MyCart: React.FC = (props) => {
const {cartList} = props
// this will be re-evaluated on every render
const totalAmount = cartList.reduce((acc, item) => {
return acc + item.price
}, 0)
// this will be re-evaluated only when cartList changes
const efficientTotalAmount = useCallback(() => {
return cartList.reduce((acc, item) => {
return acc + item.price
}, 0)
}, [cartList])
return (
<Fragment>
<p>Total cart amount : {efficientTotalAmount}</p>
</Fragment>
)
}
3. memoized Components
Suppose you have a Profile component. It includes 5 children components. So you might have noticed that even if the props of one child component have changed, all 5 will re-render. Have you thought about WHY? ๐ค
It is because other children's components are not memoized.
Technically we can understand a memo as It lets you skip re-rendering a component when its props are unchanged.
It is also a kind of caching. Caching of components. Here when the parent component re-renders, it rerenders all of its children. React also does that. But if the component is memoized, React will return the unchanged cached copy of the component. This will eventually same computation time and in return, we'll get SPEED.โก
export const MyProfile: React.FC = (props) => {
const {userId} = props
const [cartList, setCartList] = useState([])
const getCartListData =useCallback(async (userId:number) => {
return await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
},[])
useEffect(() => {
getCartListData(userId).then((data) => {
setCartList(data)
})
}, [userId])
// MyCart component will be re-rendered only when cartList changes
return (
<Fragment>
<MyCart cartList={cartList} />
</Fragment>
)
}
export const MyCart: React.FC = memo((props) => {
const {cartList} = props
// this will be re-evaluated on every render
const totalAmount = cartList.reduce((acc, item) => {
return acc + item.price
}, 0)
// this will be re-evaluated only when cartList changes
const efficientTotalAmount = useCallback(() => {
return cartList.reduce((acc, item) => {
return acc + item.price
}, 0)
}, [cartList])
return (
<Fragment>
<p>Total cart amount : {efficientTotalAmount}</p>
</Fragment>
)
})
4. Lazy imports of components
Making a gigantic app includes hundreds of components. Some small, some big.
To load all heavy (in terms of computation) components in one go will surely impact the app's startup time.
Wise approach would be to dynamically import the components as and when required. We also call it Lazy Loading.
Suppose there is a component called
<CropImage />
. It has some heavy libraries used in it. We can lazily load it when the user wants to open that.
import React, {Fragment} from 'react'
// importing CropImage as a lazy component
const CropImage = React.lazy(() => import('./CropImage'))
export const MyProfile: React.FC = (props) => {
// Using React.lazy and Suspense we can import the component only when it is needed
const CropImageComponent = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<CropImage />
</Suspense>
)
}
return (
<Fragment>
<CropImageComponent />
</Fragment>
)
}
Source : react-lazy-load-image-component
Closing comments ๐โโ๏ธ
Thank you for reading along with me. If you find any queries on the topic mentioned above, please ping me in the comments. ๐ฌ
Knowledge spreads by sharing. So please share this article with your concerned friends. ๐ฑ
PS: Also, I humbly request you to write which topic you want in my next blog. I will include that in my target list. ๐ฏ
Tirth Patel, signing off!๐ซก