Make your React app faster - 5 effective ways to boost performance ๐Ÿš€

ยท

5 min read

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.

  1. useCallback

  2. useMemo

  3. memoized components

  4. Components Lazy import

  5. 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>
  )
}

Lazy load image

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!๐Ÿซก

ย