ScanSkill
Sign up for daily dose of tech articles at your inbox.
Loading

Hooks: useMemo and useCallback In React

Hooks: useMemo and useCallback In React
Hooks: useMemo and useCallback In React

In this article, we will be discussing the two in-built react hooks: useMemo and useCallback. Whenever there is an update in a component, react re-renders. This may cause unchanged parts of the components to re-render unnecessarily. So, to avoid these types of unwanted re-render of the whole component tree, we use useMemo and useCallback. We implement the memoization method. It is an optimization method that stores the results of expensive functions and returns the cached results when a similar scenario takes place. Lets know about useMemo and useCallback in React

What is useMemo Hook?

useMemo is an in-built react hook that allows us to memoize expensive functions so that the whole component tree does not render on every change. useMemo returns a memoized value.

useMemo takes in a function and an array of dependencies.

Syntax

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useMemo will run the expensive operation only when one of the dependencies has been changed. If no changes have been made it will return the stored value. This optimization helps to avoid expensive calculations on every render. If no dependencies are given, it will run on every render.

Lets’s take a simple example where a user inputs a name and clicks some button.

Without useMemo

import "./App.css";
import { useMemo, useState } from "react";
import "bootstrap/dist/css/bootstrap.min.css";

function App() {
  const [value, setValue] = useState(0);
  const [name, setName] = useState("");

  const expensiveFunction = (num) => {
    console.log("Expensive function running");
    return num;
  };

  const result =  expensiveFunction(value);

  return (
      <div className="App">
      <h1>useMemo Example</h1>
      <input
        className="form-control"
        placeholder="Enter Name"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button className="btn btn-primary my-1" onClick={() => setValue(1)}>
        1
      </button>
      <button className="btn btn-primary my-1" onClick={() => setValue(2)}>
        2
      </button>
      <h3>You name is {name}</h3>
      <h3>You clicked {result}</h3>
    </div>
  );
}

export default App;

Output

Output without useMemo
Output without useMemo

Here, we can see the expensive function renders on every setName() function call. We typed the name “John”, 4 letters mean 4 setName() function calls and 1 render on button click function. so the whole component tree renders on every change in any part of the component. It reduces the performance of the app. So implementing useMemo, we can avoid such issues.

Implementing useMemo

import "./App.css";
import { useMemo, useState } from "react";
import "bootstrap/dist/css/bootstrap.min.css";

function App() {
  const [value, setValue] = useState(0);
  const [name, setName] = useState("");

  const expensiveFunction = (num) => {
    console.log("Expensive function running");
    return num;
  };

  const result = useMemo(() => {
    return expensiveFunction(value);
  }, [value]);

  return (
    <div className="App">
      <h1>useMemo Example</h1>
      <input
        className="form-control"
        placeholder="Enter Name"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button className="btn btn-primary my-1" onClick={() => setValue(1)}>
        1
      </button>
      <button className="btn btn-primary my-1" onClick={() => setValue(2)}>
        2
      </button>
      <h3>You name is {name}</h3>
      <h3>You clicked {result}</h3>
    </div>
  );
}

export default App;

Output

Output with useMemo
Output with useMemo

Here, The expensive function runs once on the initial render and only when the button value changes. The whole component tree does not render on every change. The change in input Filed does not make the expensive function to render. The expensive function renders once in the initial render of the component and only when the value in its dependency changes.

What is useCallback Hook?

useCallback is also an in-built react hook that is used to prevent the unnecessary rendering of the whole component tree when a certain part of the component is changed. useCallback returns a memoized callback.

useCallback takes in an inline callback function and an array of dependencies. It returns a memoized version of the callback that only changes if one of the dependencies has changed.

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

Syntax

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

Let’s see an example with some buttons to increment the count and display the count.

Without useCallback

import "./App.css";
import { useState } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import Header from "./Header";
import Footer from "./Footer";
import Count from "./Count";

function App() {
  const [countOne, setCountOne] = useState(0);
  const [countTwo, setCountTwo] = useState(0);

  const incrementTeamOne = () => {
    console.log("render button one")
    setCountOne(countOne + 1);
  };

  const incrementTeamTwo = () => {
    console.log("render button two")
    setCountTwo(countTwo + 1);
  };

  return (
    <div className="App">
      <Header />
      <Count label={"Team 1"} count={countOne} />
      <button className="btn btn-primary" onClick={incrementTeamOne}>
        Increment Team 1
      </button>
      <Count label={"Team 2"} count={countTwo} />
      <button className="btn btn-primary" onClick={incrementTeamTwo}>
        Increment Team 2
      </button>
      <Footer />
    </div>
  );
}

export default App;

Count.js

import React from 'react'

function Count({label , count}) {
    console.log("render count for ",label)
  return (
    <div>
        <label>{label}</label>
        <div>{count}</div>
    </div>
  )
}

export default Count

Header.js

import React from 'react'

function Header() {
    console.log("Header rendered")
  return (
    <h3>This is Header</h3>
  )
}

export default Header

Footer.js

import React from 'react'

function Footer() {
    console.log("Footer rendered")
  return (
    <h4>This is Footer</h4>
  )
}

export default Footer

Output

useMemo and useCallback
Output without useCallback

Here, as we can see the console is flooded with render messages. without implementation of useCallback, the whole component is rendered again and again whenever a change is detected anywhere in the component. when we increment 1, only the increment 1 button and team 1 count should render but without useCallback, the whole component gets re-rendered.

So, to avoid this issue, we can implement useCallback.

Implementing useCallback

import "./App.css";
import { useCallback, useState } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import Header from "./Header";
import Footer from "./Footer";
import Count from "./Count";

function App() {
  const [countOne, setCountOne] = useState(0);
  const [countTwo, setCountTwo] = useState(0);

  const incrementTeamOne = useCallback(() => {
    console.log("render button one")
    setCountOne(countOne + 1);
  },[countOne]);

  const incrementTeamTwo = useCallback(() => {
    console.log("render button two")
    setCountTwo(countTwo + 1);
  },[countTwo]);

  return (
    <div className="App">
      <Header />
      <Count label={"Team 1"} count={countOne} />
      <button className="btn btn-primary" onClick={incrementTeamOne}>
        Increment Team 1
      </button>
      <Count label={"Team 2"} count={countTwo} />
      <button className="btn btn-primary" onClick={incrementTeamTwo}>
        Increment Team 2
      </button>
      <Footer />
    </div>
  );
}

export default App;

To prevent unnecessary re-renderings of the child component, we wrap it into React.memo().

Count.js

import React from 'react'

function Count({label , count}) {
    console.log("render count for ",label)
  return (
    <div>
        <label>{label}</label>
        <div>{count}</div>
    </div>
  )
}

export default React.memo(Count)       // prevents unnecessary re-rendering

Do it for all other child components.

Output

Output with useCallBack
Output with useCallback

Here, we can see that the console messages are reduced. With the implementation of useCallback and React.memo(), the unnecessary re-rendering of the whole component and the child components is reduced. When we click the increment team 1 button only the team 1 button and the count display are rendered. similarly, when we click the increment team 2 button only the team 2 button and team 2 count is rendered.

Difference between useMemo and useCallback

The main difference between useMemo and useCallback is that useCallback returns a memoized callback function whereas useMemo returns a memoized value.

useCallback and useMemo both take a function and an array of dependencies. The difference is that useMemo calls its function and returns the result when the dependencies change while useCallback returns its function.

Conclusion

In this article, we discussed useMemo and useCallback and the differences between these two react hooks. both are used to prevent the unnecessary re-rendering of the whole component tree when changes are detected in any part of the component. useMemo returns memoized value and useCallback returns memoized callback function when the value given in dependencies changes. For more details on useMemo and useCallback, you can refer to the official documentation.

Sign up for daily dose of tech articles at your inbox.
Loading