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
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
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
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
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.