Sagar Medtiya
Sagar Medtiya

Sagar Medtiya

Some best techniques to Optimize your React App🐱‍💻

Some best techniques to Optimize your React App🐱‍💻

Learn this technique to optimize your React app!🍀

Sagar Medtiya's photo
Sagar Medtiya
·Aug 31, 2022·

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Table of contents

  • 🍀shouldComponentUpdate
  • 🍀Lazy Loading
  • 🍀Memoization
  • 🍀Points to remember

Improving the web🌐 application performance plays an important role to improve User Experience and keep users engaged. However, it can be challenging to optimize your React App for performance. This post will cover some techniques to improve your React code.

Lets see what all important best optimization techniques we can apply to the React code.

🍀shouldComponentUpdate

The shouldComponentUpdate is a lifecycle method in react. This method makes the react component to re-render whenever there are changes in state or props and that change will affect the output.

For more detailed blog, you can have a look to this blog.

It helps in checking whether re-rendering is required or not. If re-rendering is not required then shouldComponentUpdate does not render the code. For example, if you want certain code to not re-render then this method can be of very handy.

Note: It is encouraged to use functional components over class components. With functional component you can use useMemo() and React.memo hooks.

function shouldComponentUpdate(nextProps, nextState){
    return nextProps.id !== this.props.id;
}

🍀Lazy Loading

Lazy Loading.gif Lazy loading is a great technique to improve your app's performance. The idea of lazy loading is to load a component only when it is needed. React comes bundled with React.lazy API so that you can import a dynamic import to regular component.

For example,

const LazyComponent = React.lazy(() => import('./LazyComponent'));

React.lazy takes a function that must call a dynamic import(). This will then return a Promise which resolves to a module with a default export containing a React component.

Another way is to load components only when they are in the viewport. You can use this awesome library react-loadable-visibility.

import LoadableVisibilty from 'react-loadable-visibility/react-loadable';
import MyLoader from './my-loader-component'

const LoadableComponent = LoadableVisibilty({
    loader:() => import('./my-component'),
    loading:MyLoader
});

export default function APP(){
    return <LoadableComponent/>
}

🍁Suspense

Lazy Loading.png

Note: Suspense is an experimental feature at this time. Experimental features may change significantly and without a warning before they become a part of React.

Suspense is an another technique used for lazy loading. It lets you "wait" for some code to load and lets you declaratively specify a loading state while waiting. Multiple lazy components can be wrapped with the suspense component. It takes a fallback property that accepts the react elements you want to render as the lazy component is being loaded.

// install the experimental version
npm install react@experimental react-dom@experimental

import React,{Component, lazy, Suspense} from 'react'
const MyComp = lazy(()=>import('../mycomp'))
<Suspense fallback={<div>Loading...</div>}>
<div>
    <MyComp></MyComp>    
</div>
  • Suspense is most popularly used for waiting for the data
  • But it can also be used to wait for images, script or any asynchronous code.
  • It helps you avoid race conditions
    • Race conditions are bug that happen due to incorrect assumptions about the order in which our code can run.
    • Suspense feels more like reading data synchronously -- as if it was already loaded.

🍁Suspense - Data Fetching

  • It is used to wait for rendering component until the required data is fetched.
    • That means no need to add conditional code anymore!
  • You need to enable concurrent mode.
    • So the rendering is not blocked.
    • This gives better user experience.


// enable concurrent mode 
const rootEl = document.getElementById('root')

//ReactDOM.render(<APP/>, rootEl)
const root = ReactDom.createRoot(rootEl) //you'll use a new createRoot API

root.render(<APP/>)

🍁Render-as-you-Fetch

This approach doesn't wait for the response to come back before we start rendering. We start rendering pretty much immediately after kicking off the network request.

  • Start fetching
  • Start rendering
  • finish fetching
const resource = fetchCartData();

function CartPage(){
    return (
        <Suspense fallback={<h1>Loading Cart...</h1>}>
            <CartDetails/>
        </Suspense>
    );
}
function CartDetails(){
    //Try to read product info, although it might not have loaded yet
    const product = resource.product.read();
    return <h1>{product.name}</h1>
}

The CartPage() is loaded. It tries to load CartDetails(), but CartDetails() makes call to resource.product.read() so this component suspenses. React shows the fallback loader and keep fetching the data in the background. When all the data is retrieved the fallback loader is replaced by CartDetails() children.

🍀Memoization

Lazy Loading (1).png

Memoizing in React is a performance feature of the framework that aims to speed up the render process of component. It is a well know concept in programming, aiming to speed up the program by caching results of the expensive function and re-using those cached result as to avoid rendering of expensive function again.

Lazy Loading (2).png

Memoization in React is not guarantee that your function will be cached

🍁When to use it?

Suppose your state object is updating but your value is not really changing, you can use memo to not re-render your functional component. If your component just takes primitive value as props, just wraps it in memo() to prevent an unwanted re-render.

React.memo() by default just compares the top-level props. It is not that simple for nested objects. For nested objects, you can pass custom comparer function to check if prop values are same.

export default React.memo((props)=>{
    return (<div>{props.val}</div>)
})

//for nested object
const MemoedElement = React.memo(Element, areEqual) 

export function areEqual(prevProps: props, nextProps:props){
    const cardId = nextProps.id
    const newActiveCardId = nextProps.newActiveCardId
    const isActive = newActiveCardId == cardId

    return !some([
        isActive,
    ])
}

🍁useMemo

React.memo is used to memoize components. You can use useMemo to memoize inner variable. If there's CPU intensive operation going on to calculate those variables, and the variables does not really change that often then you can use useMemo.

const allItems = getItems()
//CPU intensive logic
const itemCategories = useMemo(()=> getUniqueCategories(allItems),[allItems])

🍀Points to remember

  • Measure performance using these tools
    • Chrome dev tools
      - Play with the throttle feature
      - Check out the performance timeline and flame charts
      
    • Chrome's Lighthouse tool
  • Minimise unnecessary component re-renders
    • use shouldComponentUpdate where applicable
    • use PureComponent
    • use React.memo for functional components
      - along with the ```useMemo()``` hook
      
    • use React.lazy if you not doing server-side rendering
    • use service worker to cache files that are worth caching
    • use libraries like react-snap to pre-render components

Thanks for reading. Like for more such blogs. Happy coding💗

Did you find this article valuable?

Support Sagar Medtiya by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this