Open In App

How to Memoize with React.useMemo() ?

Last Updated : 11 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

React provides a powerful tool for optimizing performance, the useMemo() hook. In this article, we’ll delve into how useMemo() works, its benefits, and practical examples of how to manipulate it effectively in your React applications.

Understanding useMemo() Hook in React:

The useMemo() hook is used to memoize the result of a function computation, ensuring that the computation is only re-executed when its dependencies change. This can significantly improve performance by preventing unnecessary recalculations, particularly in scenarios where expensive computations are involved.

Benefits of useMemo() Hook in React:

  • Performance Optimization: By memoizing expensive function results, useMemo() reduces unnecessary re-renders and computations, thereby improving overall application performance.
  • Preventing Unnecessary Work: useMemo() ensures that expensive computations are only performed when necessary, optimizing the utilization of resources and reducing unnecessary work.
  • Enhanced Responsiveness: With useMemo(), React components can respond more quickly to user interactions and data changes, resulting in a smoother user experience.

Example of memoizing with useMemo Hook in React

Example 1: Optimizing Complex Calculations:

  • Let’s suppose you have a user interface where you enter a number, and it gets doubled and displayed. Plus, the background color changes with a theme switch.
  • Now, if doubling the number takes a hefty 2 seconds, each time you tweak the input, there’s a frustrating 2-second lag due to the slow calculation.
  • But here’s the kicker: even changing the theme, which has nothing to do with the number, triggers another 2-second delay because it causes a rerender.

Javascript




import React, { useState } from "react";
 
export const Check = () => {
    const [state, setState] = useState(0);
    const [theme, setTheme] = useState(true);
 
    const slowFunction = (num) => {
        for (var i = 0; i < 1000000000; i++) { }
        return num * 2;
    };
 
    const doubleState = slowFunction(state);
 
    return (
        <div className="simple-div">
            <center>
                <div>
                    <input onChange={(e) =>
                        setState(e.target.value)} />
                </div>
                <div style={{
                    backgroundColor: theme ? "green" : "white"
                }}>
                    <b>Value without memoizing: {doubleState}</b>
                </div>
                <button onClick={() =>
                    setTheme((prev) => !prev)}>
                    Change Theme
                </button>
            </center>
        </div>
    );
};


Output:

gfg1

Performance issue without useMemo()

This problem can be fixed using useMemo() function which will alow us to Memoize the doubling function. This means it only recalculates when you change the input, skipping unnecessary delays from unrelated changes like theme shifts.

Javascript




import React, { useState, useMemo } from "react";
 
export const App = () => {
    const [state, setState] = useState(0);
    const [theme, setTheme] = useState(true);
 
    const slowFunction = (num) => {
        for (var i = 0; i < 1000000000; i++) { }
        return num * 2;
    };
    const doubleState = useMemo(() => {
        return slowFunction(state);
    }, [state]);
 
    return (
        <div className="simple-div">
            <center>
                <div>
                    <input onChange={(e) =>
                        setState(e.target.value)} />
                </div>
                <div style={{
                    backgroundColor: theme ? "green" : "white"
                }}>
                    <b>Value without memoizing: {doubleState}</b>
                </div>
                <button onClick={() =>
                    setTheme((prev) => !prev)}>
                    Change Theme
                </button>
            </center>
        </div>
    );
};


Output:

gfg2

Performance improved using useMemo()

Example 2: Optimize Complex Calculations within Render Props or Higher-order Components using useMemo():

  • Let’s accomplish the same task using Higher order functions (HOF). In this approach, we’re using withMemoizedCalculation, which uses currying.
  • We first call complexResult, then pass its result to ComponentUsingMemoizedCalculation. However, since complexResult takes a long time to compute, it affects the theme state, as shown in the output below.

Javascript




import React, { useState } from "react";
 
const withNonMemoizedCalculation = (calculateFn) =>
    (WrappedComponent) => {
        return function WithNonMemoizedCalculation(props) {
            const result = calculateFn(props);
            return <WrappedComponent result={result}
                {...props} />;
        };
    };
 
const complexResult = (props) => {
    for (let i = 0; i < 1000000000; i++) { }
    return props.value * 2;
};
 
const ComponentUsingCalculation = ({ result, theme }) => {
    return (
        <b style={{ backgroundColor: theme && "green" }}>
            Result in HOF: {result}
        </b>
    );
};
 
const NonMemoizedComponent = withNonMemoizedCalculation(complexResult)(
    ComponentUsingCalculation
);
 
export const App = () => {
    const [state, setState] = useState(0);
    const [theme, setTheme] = useState(false);
    return (
        <div className="simple-div">
            <center>
                <div>
                    <input onChange={(e) =>
                        setState(e.target.value)} />
                </div>
                <div>
                    <NonMemoizedComponent value={state} theme={theme} />
                </div>
                <button onClick={() =>
                    setTheme((prev) => !prev)}>
                    Change Theme
                </button>
            </center>
        </div>
    );
};
 
export default App;


Output:

hofresult

Performance issue in HOF without useMemo()

Now, let’s resolve this by employing useMemo(). We’ll memoize the complexResult function based on the state of the props’ value, ensuring it doesn’t impact the theme state.

Javascript




import React, { useMemo, useState } from "react";
 
const withMemoizedCalculation = (calculateFn) =>
    (WrappedComponent) => {
        return function WithMemoizedCalculation(props) {
            const result = useMemo(() => calculateFn(props),
                [props.value]);
            return <WrappedComponent result={result}
                {...props} />;
        };
    };
 
const complexResult = (props) => {
    for (let i = 0; i < 1000000000; i++) { }
    return props.value * 2;
};
 
const ComponentUsingMemoizedCalculation = ({ result,
    theme }) => {
    return (
        <b style={{ backgroundColor: theme && "green" }}>
            Memoized result in HOF: {result}
        </b>
    );
};
 
const MemoizedComponent = withMemoizedCalculation(complexResult)(
    ComponentUsingMemoizedCalculation
);
 
export const App = () => {
    const [state, setState] = useState(0);
    const [theme, setTheme] = useState(false);
    return (
        <div className="simple-div">
            <center>
                <div>
                    <input onChange={(e) => setState(e.target.value)} />
                </div>
                <div>
                    <MemoizedComponent value={state} theme={theme} />
                </div>
                <button onClick={() =>
                    setTheme((prev) => !prev)}>
                    Change Theme
                </button>
            </center>
        </div>
    );
};
 
export default App;


Output:

hofmemoized

Example 3: Optimize Complex Calculations within Custom Hooks using useMemo(): Let’s accomplish the same task using Custom Hooks:

Here, we’re utilizing a straightforward custom hook called useExpensiveCalculations. This hook computes a result that takes a considerable amount of time, ultimately impacting the theme state.

Javascript




import React, { useState } from "react";
 
const useExpensiveCalculations = (value) => {
    const complexResult = () => {
        for (let i = 0; i < 1000000000; i++) { }
        return value * 2;
    };
    const result = complexResult();
    return { result: result };
};
 
export const App = () => {
    const [state, setState] = useState(0);
    const [theme, setTheme] = useState(false);
    const { result } = useExpensiveCalculations(state);
 
    return (
        <div className="simple-div">
            <center>
                <div>
                    <input onChange={(e) =>
                        setState(e.target.value)} />
                </div>
                <div>
                    <b style={{ backgroundColor: theme && "green" }}>
                        Memoized Result using custom Hooks: {result}
                    </b>
                </div>
                <button onClick={() =>
                    setTheme((prev) => !prev)}>
                    Change Theme
                </button>
            </center>
        </div>
    );
};
 
export default App;


Output:

hookResult

Performance issue without useMemo() in custom hook

Now similarly we will use useMemo() to optimize the hook on re-renders.

Javascript




import React, { useMemo, useState } from "react";
 
const useExpensiveCalculations = (value) => {
    const complexResult = () => {
        for (let i = 0; i < 1000000000; i++) { }
        return value * 2;
    };
 
    const result = useMemo(() => complexResult(),
        [value]);
 
    return { result: result };
};
 
export const App = () => {
    const [state, setState] = useState(0);
    const [theme, setTheme] = useState(false);
    const { result } = useExpensiveCalculations(state);
 
    return (
        <div className="simple-div">
            <center>
                <div>
                    <input onChange={(e) =>
                        setState(e.target.value)} />
                </div>
                <div>
                    <b style={{ backgroundColor: theme && "green" }}>
                        Memoized Result using custom Hooks: {result}
                    </b>
                </div>
                <button onClick={() =>
                    setTheme((prev) => !prev)}
                >Change Theme
                </button>
            </center>
        </div>
    );
};
 
export default App;


Output:

hookMemoized

Performance improved using useMemo() in custom hook

Conclusion:

useMemo() is a powerful tool for optimizing performance in React applications by memoizing expensive function results. By leveraging useMemo(), you can prevent unnecessary re-renders and computations, resulting in a smoother and more responsive user experience. Whether you’re computing Fibonacci numbers, filtering lists, or performing other computationally intensive tasks, useMemo() can be a valuable ally in your quest for performance optimization in React.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads