The useCallback
and useMemo
hooks are 2 more advanced hooks that are added in addition to the basic useState
and useEffect
hooks.
They’re both useful in different ways.
In this article, we’ll look at how to use the useCallback
and useMemo
hooks in our React components.
useCallback
The useCallback
hook is used for memoizing callbacks that we may call multiple times.
Using the useCallback
hook, we can cache callbacks that are in a component so that they won’t be created more than once unless a state or prop changes.
For instance, instead of writing:
import React from "react";
export default function App() {
const handleClick = () => {
console.log("Clicked");
};
return <button onClick={handleClick}>Click Me</button>;
}
We can write:
import React, { useCallback } from "react";
export default function App() {
const handleClick = useCallback(() => console.log("Clicked"), []);
return <button onClick={handleClick}>Click Me</button>;
}
We pass in our callback to the useCallback
hook so that the function we passed in the first argument is called when we click on the button.
But the handleClick
function is only created once instead of every render.
The 2nd argument is an array where we can pass in the items that we can watch in the function.
For instance, if we write:
import React, { useCallback, useState } from "react";
export default function App() {
const [num, setNum] = useState(0);
const increment = useCallback(() => setNum((c) => c + 1), []);
const handleClick = useCallback(() => console.log(num), []);
return (
<>
<button onClick={increment}>Increment</button>
<button onClick={handleClick}>Click Me</button>
</>
);
}
The handleClick
function will always log 0 since the function is only created when the component is mounted as we have an empty array in the 2nd argument.
However, if we pass in [num]
to useCallback
:
import React, { useCallback, useState } from "react";
export default function App() {
const [num, setNum] = useState(0);
const increment = useCallback(() => setNum((c) => c + 1), []);
const handleClick = useCallback(() => console.log(num), [num]);
return (
<>
<button onClick={increment}>Increment</button>
<button onClick={handleClick}>Click Me</button>
</>
);
}
then the console log shows the latest value of num
when we click on Click Me.
This is because the handleClick
function is re-created when num
changes since we passed in [num]
in the 2nd argument of useCallback
.
useMemo
The useMemo
hook lets us compute a value that’s derived from existing states when any of the states that it depends on changes.
For instance, we can write:
import React, { useCallback, useMemo, useState } from "react";
export default function App() {
const [num1, setNum1] = useState(0);
const [num2, setNum2] = useState(0);
const increment1 = useCallback(() => setNum1((c) => c + 1), []);
const increment2 = useCallback(() => setNum2((c) => c + 1), []);
const sum = useMemo(() => num1 + num2, [num1, num2]);
return (
<>
<button onClick={increment1}>increment 1</button>
<button onClick={increment2}>increment 2</button>
<p>num1: {num1}</p>
<p>num2: {num2}</p>
<p>sum: {sum}</p>
</>
);
}
We have the num1
and num2
states which are changed when we click the buttons.
Then we define the sum
variable, which is assigned from the useMemo
hook.
We pass in a function that returns num1
and num2
added together.
In the 2nd argument of useMemo
, we pass in num1
and num2
, which we’re watching.
It means that when num1
or num2
changes, the returned value of useMemo
callback will be assigned to sum
.
Therefore, when we click on either button, sum
changes.
Conclusion
We can use useCallback
to cache functions and only recreate them when a state or prop changes.
The useMemo
lets create variables derived from state or prop values and cache them until any dependent value changes.