Understanding useMemo in React

When the state changes in a React component, the entire component re-renders, causing all functions and data to be recalculated. This can slow down our application, especially if there are expensive calculations involved.

useMemo

When state change our entire component re-render, so all the functions and data will be re-render and slow down our app

import { useState } from "react"

const UseMemo = () => {
    const [number, setnumber] = useState(0)
    const [dark, setdark] = useState(false)
    const dblNumber = slowFunc(number)

    const themestyle = {
        background: dark ? 'black' : 'white',
        color: dark ? 'white' : 'black'
    }

    return ( <>
            <input type="number" value={number} 
                    onChange={(e) => setnumber(parseInt(e.target.value))} />
            <button onClick={() => setdark(prevVal => !prevVal)}>Change Theme</button>
            <div style={themestyle}>{dblNumber}</div>
    </> )
}

const slowFunc = (n) => {
    console.log('Function Start')
    for (let i = 0; i <= 1000000000; i++) { null }
    console.log('Function End')
    return n * 2
}

export default UseMemo

We create input that value is number, theme changing button and display data div. Here we see when we change the value of input function call and data load more but when we change theme that time also data load and slow down our component.

So here we see only data load when numbers change, not that time when themes change.

We useMemo to memorise changes.

First time we change the number that time value from 0 to 1 calculate and ( value change so) function run, and return value stored in memory, But when we change theme that time value is 1 and output is stored in memory so useMemo hook easily gives value without run function.

So when we change the theme that time slowFunc value store in memory useMemo hook.

const dblNumber = useMemo(() => {
    return slowFunc(number)
}, [number])

Now when we change the number that time runs the function, otherwise it gives value from memory.

We see in Javascript value vs references, In case of Object and Array same value two object or array are different.

const val1 = { bg: 'grey', val: 0 } and const val2 = { bg: 'grey', val: 0 }

These two objects are different, their values are the same but their references are different.

Now we have this problem in react also. So we solve using the useEffect hook. When the themestyle changes that time render otherwise no, but component re-render and objects and array are also re-render.

useEffect(() => {
    console.log('Theme Changes')
}, [themestyle])

We have a themestyle object whose value is the same for every time in the component but when we change state value themestyle also re-create, when data is big that reduces our app quality, So we use memo for that type of object.

const themestyle = useMemo(() => { return {
        background: dark ? 'black' : 'white',
        color: dark ? 'white' : 'black'
} }, [dark])


useEffect(() => {
    console.log('Theme Changes')
}, [themestyle])
import { useState, useMemo } from "react";

const UseMemo = () => {
    const [number, setNumber] = useState(0);
    const [dark, setDark] = useState(false);
    const dblNumber = useMemo(() => slowFunc(number), [number]);

    const themeStyle = useMemo(() => ({
        background: dark ? 'black' : 'white',
        color: dark ? 'white' : 'black'
    }), [dark]);

    return (
        <>
            <input 
                type="number" 
                value={number} 
                onChange={(e) => setNumber(parseInt(e.target.value))} 
            />
            <button onClick={() => setDark(prevDark => !prevDark)}>Change Theme</button>
            <div style={themeStyle}>{dblNumber}</div>
        </>
    );
};

const slowFunc = (n) => {
    console.log('Function Start');
    for (let i = 0; i <= 1000000000; i++) { null }
    console.log('Function End');
    return n * 2;
};

export default UseMemo;

In the above component, changing the input value triggers the slowFunc function, which performs a time-consuming calculation. Additionally, changing the theme also causes the function to run, impacting the component's performance.

To optimize performance and prevent unnecessary recalculations, we can use the useMemo hook. This hook memoizes the result of a function, returning the cached result when the dependencies remain unchanged.

Info

useMemo is useful for optimizing expensive computations or preventing unnecessary re-renders in React components.

Usage of useMemo

We use useMemo to memoize the result of the slowFunc function, ensuring that it only runs when the number state changes.

const dblNumber = useMemo(() => slowFunc(number), [number]);

Additionally, we use useMemo to memoize the theme style object, preventing it from being recreated on every render when the dark state changes.

const themeStyle = useMemo(() => ({
    background: dark ? 'black' : 'white',
    color: dark ? 'white' : 'black'
}), [dark]);

Further Optimization with useEffect

In some cases, objects or arrays may be recreated unnecessarily on every render, even when their values remain the same. We can further optimize performance by using useMemo in conjunction with useEffect to memoize such objects.

useEffect(() => {
    console.log('Theme Changes');
}, [themeStyle]);

By memoizing the themeStyle object, we ensure that the useEffect hook only runs when the theme changes, rather than on every render.

Info

Optimizing performance in React is essential for maintaining a smooth user experience. Use useMemo and useEffect judiciously to prevent unnecessary recalculations and re-renders.

Task

Practice Using useMemo

Objective: Optimize performance by memoizing expensive computations or objects using the useMemo hook.

  1. Memoize Expensive Computation:

    Create a new file ExpensiveComputation.jsx and define a component that performs a time-consuming calculation.

    // ExpensiveComputation.jsx
    import React, { useState, useMemo } from 'react';
    
    const ExpensiveComputation = () => {
        const [number, setNumber] = useState(0);
        const [result, setResult] = useState(0);
    
        const calculateResult = (n) => {
            // Simulate a time-consuming calculation
            for (let i = 0; i < 1000000000; i++) { }
            return n * 2;
        };
    
        const memoizedResult = useMemo(() => calculateResult(number), [number]);
    
        const handleInputChange = (e) => {
            setNumber(parseInt(e.target.value));
        };
    
        const handleButtonClick = () => {
            setResult(memoizedResult);
        };
    
        return (
            <div>
                <input 
                    type="number" 
                    value={number} 
                    onChange={handleInputChange} 
                />
                <button onClick={handleButtonClick}>Calculate Result</button>
                <p>Result: {result}</p>
            </div>
        );
    };
    
    export default ExpensiveComputation;
    
  2. Test Performance:

    Import and use the ExpensiveComputation component in your App component to test the performance improvement achieved by memoizing the calculation using useMemo.

    // App.jsx
    import React from 'react';
    import ExpensiveComputation from './ExpensiveComputation';
    
    const App = () => {
        return (
            <div>
                <h1>Optimizing Performance with useMemo</h1>
                <ExpensiveComputation />
            </div>
        );
    };
    
    export default App;
    
  3. Measure Performance:

    Use browser developer tools or performance monitoring tools to measure the difference in rendering time between the memoized and non-memoized versions of the calculation.

    Note: Compare the rendering time before and after using useMemo to observe the performance improvement.

This task will help you practice using the useMemo hook to optimize performance in React components by memoizing expensive computations.