Let’s dive right in, shall we?
useMemo:
- stores the result of a calculation between re-renders (e.g. result of a function call)
- caching return values is known as memoization
- syntax: `useMemo(calculateValue, dependencies)
- returns the result of calling
calculateValue
Re-rendering:
Whenever a Component is rerendered, objects and arrays declared directly within the component is also recreated/redeclared.
This is also true for function declarations like function(){}
and expressions like () => {}
- a new, different function is created on every re-render.
In other words: Let’s say you have a function handleSubmit
within App
component and you’d like to pass it to App
’s child component, Section
.
const App = () => {
const handleSubmit = () => {
// ... some code
}
return <Section onSubmit={handleSubmit} />;
}
Let’s assume that App
has state that changes and triggers a re-render. Upon re-rendering, the function handleSubmit()
is also RECREATED into a brand new, different function. This is usually … Fine. However, if you memoized <Section />
in this case, Section
will think that {handleSubmit} is a different prop every time, and will be recalculated.
(If you want to store a function and not have it be re-declared into a new, different function unless its dependency changes, you would turn to useCallback
).
calculateValue
- function that calculates whatever you want to store
- should NOT take arguments & returns a value of any type
- function is called during initial render
- this function runs again only when dependencies have changed
Console.time
If you want to tell if a certain calculation is expensive, you can use console.time('start')
and console.timeEnd('end')
to measure the time spent in a piece of code.
React.dev suggests that if the overall logged time is 1ms
or more, using useMemo
might be a good idea. But note, useMemo
doesn’t make the first render faster - it only helps you skip it from running again if the values are the same.
Don’t overuse useMemo
You should wrap the result of an operation with useMemo if re-doing or repeating that operation takes a lot of time. If the code without memoization is fast enough, you don’t necessarily need memoization.
Note about dependency array in useMemo
:
If you are passing an object created directly in the component body as useMemo
’s dependency array, memoization might be pointless.
Look at the code provided by React.dev below:
function Dropdown({ allItems, text }) {
const searchOptions = { matchMode: 'whole-word', text };
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // 🚩 Caution: Dependency on an object created in the component body
// ...
Here, searchOptions
is an object declared directly in Dropdown
Component. Objects and arrays declared directly within the component body are essentially re-run and recreated into new objects when a component re-renders for any reason.
In this case, the value of visibleItems
will be recalculated every time Dropdown
re-renders because searchOptions
is also recreated and therefore a distinctly different object from the previous searchOptions.
Since searchOptions
is a new object, recalculation takes place.
To remedy this, one option might be to memoize the searchOptions
and pass the text
as its dependency prior to passing it as visibleItems
’s dependency, like so:
(Code from react.dev)
function Dropdown({ allItems, text }) {
const searchOptions = useMemo(() => {
return { matchMode: 'whole-word', text };
}, [text]); // ✅ Only changes when text changes
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // ✅ Only changes when allItems or searchOptions changes
// ...
Alternatively, you can also place searchOptions
inside the useMemo
hook.
function Dropdown({ allItems, text }) {
const visibleItems = useMemo(() => {
const searchOptions = { matchMode: 'whole-word', text };
return searchItems(allItems, searchOptions);
}, [allItems, text]); // ✅ Only changes when allItems or text changes
// ...
What happens if I don’t pass anything in the dependency array?
useMemo will be re-running the calculation every time anything changes or is re-rendered.
useCallback
- basic syntax:
useCallBack(function, dependencies)
- A React Hook that lets you cache a function definition between re-renders. Basically, prevents the function to be redeclared (and keep the same cached instance of the function).
- You wrap the function you want to cache with
useCallback
. - Concept is really similar to
useMemo
, exceptuseMemo
stores the calculation or the result of a function call or operation.
return
- Initial render:
useCallback
returns thefunction
function you have passed. - Subsequent renders: returns an already stored
function
function so long as the dependencies passed touseCallback
have not changed. Or, it returns thefunction
function you’ve passed during this render.