Open In App

When to use useCallback, useMemo and useEffect ?

Improve
Improve
Like Article
Like
Save
Share
Report

The useCallback, useMemo, and useEffect are used to optimize the performance of React-based applications between rerendering of the components. To answer when to use useCallBack, useMemo, and useEffect, we should know what exactly they do and how they are different.

Prerequisites

useCallback: The useCallback is a react hook that returns a memoized callback when passed a function and a list of dependencies as parameters. It’s very useful when a component is passing a callback to its child component to prevent the rendering of the child component. It only changes the callback when one of its dependencies gets changed.

useMemo: The useMemo is similar to useCallback hook as it accepts a function and a list of dependencies but it returns the memoized value returned by the passed function. It recalculated the value only when one of its dependencies change. It is useful to avoid expensive calculations on every render when the returned value is not going to change.

useEffect: The useEffect hook that helps us to perform mutations, subscriptions, timers, logging, and other side effects after all the components has been rendered. The useEffect accepts a function that is imperative in nature and a list of dependencies. When its dependencies change it executes the passed function.

Steps to create React application for understanding all the three hooks:

Step 1: Create a React application using the following command:

npx create-react-app usecallbackdemo

Step 2: After creating your project folder i.e. foldername, move to it using the following command:

cd usecallbackdemo

Project Structure:

The project structure

The updated Dependencies in package.json file will look like:

"dependencies": {
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
        "react-scripts": "5.0.1",
        "web-vitals": "^2.1.4"
}

Now let’s understand the working of all three hooks.

1. usecallback: It depends on referential equality. In javascript, functions are first-class citizens, meaning that a function is a regular object. Hence, two function objects even when they share the same code are two different objects. Just remember that a function object is referentially equal only to itself.

Example: Write the following code in App.js and List.js file

Javascript




//App.js
 
import React, { useState } from "react"
import List from "./List"
 
function App() {
 
    {/* Initial states */ }
    const [input, setInput] = useState(1);
    const [light, setLight] = useState(true);
 
    {/* getItems() returns a list of number which
    is number+10 and number + 100 */}
    const getItems = () => {
        return [input + 10, input + 100];
    }
 
    {/* Style for changing the theme */ }
    const theme = {
        backgroundColor: light ? "White" : "grey",
        color: light ? "grey" : "white"
    }
 
    return <>
        {/* set the theme in the parent div */}
        <div style={theme}>
            <input type="number"
                value={input}
 
                {/* When we input a number it is store in our stateful variable */}
                onChange={event => setInput(parseInt(event.target.value))} />
 
            {/* on click the button the theme is set to the
            opposite mode, light to dark and vice versa*/}
            <button onClick={() => setLight(prevLight => !prevLight)}>
                {light ? "dark mode" : "light mode"}
            </button>
            <List getItems={getItems} />
        </div>
    </>;
}
 
export default App;


Javascript




//List.js
 
import React, { useEffect, useState } from "react"
 
function List({ getItems }) {
 
    /* Initial state of the items */
    const [items, setItems] = useState([]);
 
    /* This hook sets the value of items if
       getItems object changes */
    useEffect(() => {
        console.log("Fetching items");
        setItems(getItems());
    }, [getItems]);
 
    /* Maps the items to a list */
    return <div>
        {items.map(item => <div key={item}>{item}</div>)}
    </div>
}
export default List;


Step to run the application:

npm start

Output:The list component gets the getItems function as a property. Every time the getItems function object changes useEffect will call setItems to set the list returned from the function object to stateful variable items and then we map those items into a list of div.Every time items are fetch using getItems in useEffect, we print “Fetching items” to see how often the items are fetched.

Now the weird thing is, when we press the button to change the theme, we see that the items are still being fetched even when the input field is not modified because it is called every time the component is re-rendered.

Solution: Using useCallback

Javascript




//App.js
 
import React, { useCallback, useState } from "react"
import List from "./List"
 
function App() {
 
    {/* Initial states */ }
    const [input, setInput] = useState(1);
    const [light, setLight] = useState(true);
 
    {/* useCallback memoizes the getItems() which
       returns a list of number which is number+10
       and number + 100 */}
    const getItems = useCallback(() => {
        return [input + 10, input + 100];
    }, [input]);
 
    {/* style for changing the theme */ }
    const theme = {
        backgroundColor: light ? "White" : "grey",
        color: light ? "grey" : "white"
    }
 
 
    return <>
        {/* set the theme in the parent div */}
        <div style={theme}>
            <input type="number"
                value={input}
 
                {/* When we input a number it is stored in
            our stateful variable */}
                onChange={event =>
                    setInput(parseInt(event.target.value))
                } />
 
            {/* on click the button the theme is set to
            the opposite mode, light to dark and vice versa*/}
            <button onClick={() =>
                setLight(prevLight =>
                    !prevLight)}>{light ? "dark mode" : "light mode"}
            </button>
            <List getItems={getItems} />
        </div>
    </>;
}
 
export default App;


Output:

2. useMemo: The useMemo hook returns a memoised value after taking a function and a list of dependencies. It returns the cached value if the dependencies do not change. Otherwise, it will recompute the value using the passed function.

Example: Doing heavy calculation without useMemo

Javascript




//App.js
 
import React, { useState } from 'react';
 
const WithoutMemo = () => {
    const [count, setCount] = useState(0);
    const [renderCount, setRenderCount] = useState(0);
 
    const computeExpensiveValue = (num) => {
        console.log("Computing...");
        let result = 0;
        for (let i = 0; i < 1000000000; i++) {
            result += num;
        }
        return result;
    };
 
    const result = computeExpensiveValue(count);
 
    // This component re-renders on every count change,
    // causing the expensive function to run again
    return (
        <div>
            <h2>Without Memo Example</h2>
            <p>Count: {count}</p>
            <p>Result: {result}</p>
            <p>Render Count: {renderCount}</p>
            <button onClick={() => setCount(count + 1)}>Increment Count</button>
            <button onClick={() => setRenderCount(renderCount + 1)}>
                Increment Render Count
            </button>
        </div>
    );
};
 
export default WithoutMemo;


Output:

Animation14Here we can see that it is taking too much time to calculate the result when rendered.

Solution: Using UseMemo

Javascript




//App.js
 
import React, { useState, useMemo } from 'react';
 
const WithMemo = () => {
    const [count, setCount] = useState(0);
    const [renderCount, setRenderCount] = useState(0);
 
    const computeExpensiveValue = (num) => {
        console.log("Computing...");
        let result = 0;
        for (let i = 0; i < 1000000000; i++) {
            result += num;
        }
        return result;
    };
 
    // Using useMemo to memoize the result based on count
    const result = useMemo(() => computeExpensiveValue(count), [count]);
 
    return (
        <div>
            <h2>With Memo Example</h2>
            <p>Count: {count}</p>
            <p>Result: {result}</p>
            <p>Render Count: {renderCount}</p>
            <button onClick={() => setCount(count + 1)}>Increment Count</button>
            <button onClick={() => setRenderCount(renderCount + 1)}>
                Increment Render Count
            </button>
        </div>
    );
};
 
export default WithMemo;


Output:

Animation15

Explanation: Here we can see when clicking on “Increment render count” component without useMemo takes too much time.

3. useEffect: In react, side effects of some state changes are not allowed in functional components. To perform a task once the rendering is complete and some state changes, we can use useEffect. This hook takes a function to be executed and a list of dependencies, changing which will cause the execution of the hook’s body.

Example:

Javascript




//App.js
 
import React, { useEffect, useState } from "react"
 
 
function App() {
 
    /* Some data */
    const data = {
        Colors: ["red", "green", "yellow"],
        Fruits: ["Apple", "mango", "Banana"]
    }
 
    /* Initial states */
    const [currentChoice, setCurrentChoice] = useState("Colors");
    const [items, setItems] = useState([]);
 
    /* Using useEffect to set the data of currentchoice
       to items and console log the fetching... */
    useEffect(() => {
        setItems(data[currentChoice]);
        console.log("Data is fetched!");
    }, [currentChoice]);
 
    return <>
        <button onClick={() => setCurrentChoice("Colors")}>Colors</button>
        <button onClick={() => setCurrentChoice("Fruits")}>Fruits</button>
        {items.map(item => { return <div key={item}>{item}</div> })}
    </>;
}
 
export default App;


Output:

Animation16

Explanation:  When the application loads for the first time, data is fetched from our fake server. This can be seen in the console in the image below. And when we press the Fruits button, appropriate data is again fetched from the server and we can see that “Data is fetched” is again printed in the console. But if we press the colors button again and again, we don’t have to get the data from the server again as our choice state does not change.

Conclusion:

Hence, a useCallback hook should be used when we want to memoize a callback, and to memoize the result of a function to avoid expensive computation we can use useMemo. useEffect is used to produce side effects to some state changes.One thing to remember is that one should not overuse hooks. 



Last Updated : 12 Dec, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads