145 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						||
tags:
 | 
						||
  - javascript
 | 
						||
  - react
 | 
						||
---
 | 
						||
 | 
						||
# Memoization with useCallback and useMemo
 | 
						||
 | 
						||
## Rationale
 | 
						||
 | 
						||
In the lifecycle of a component, React re-renders the component when an update
 | 
						||
is made. When React checks for any changes in a component, it may detect an
 | 
						||
unintended or unexpected change due to how JavaScript handles equality and
 | 
						||
shallow comparisons. This change in the React application will cause it to
 | 
						||
re-render unnecessarily.
 | 
						||
 | 
						||
Additionally, if that re-rendering is an expensive operation, like a long for
 | 
						||
loop, it can hurt performance. Expensive operations can be costly in either
 | 
						||
time, memory, or processing. In addition to potential technical issues, this may
 | 
						||
lead to poor user experience.
 | 
						||
 | 
						||
If one part re-renders, it re-renders the entire component tree. `useCallback`
 | 
						||
and `useMemo` can be used reduce this performance impact.
 | 
						||
 | 
						||
## useCallback
 | 
						||
 | 
						||
The `useCallback` hook is used to wrap functions. It tells React to not
 | 
						||
re-create a wrapped function when a component re-renders, unless any of the
 | 
						||
useCallback's dependencies change.
 | 
						||
 | 
						||
`useCallback` returns a memoized version of the callback function it is passed.
 | 
						||
This means that the function object returned from useCallback will be the same
 | 
						||
between re-renders.
 | 
						||
 | 
						||
Remember that in JavaScript, functions are objects and components are functions.
 | 
						||
As a result, every time a component containing a function re-renders, it create
 | 
						||
a new instance of the function in memory.
 | 
						||
 | 
						||
> Given the same dependency value, the `useCallback` hook returns the same
 | 
						||
> function instance between renderings (aka memoization).
 | 
						||
 | 
						||
This said, for small functions that are not intensive, it doesn't really matter
 | 
						||
if they are not memoized.
 | 
						||
 | 
						||
### Syntax
 | 
						||
 | 
						||
A standard case of this would be a function that runs on a button click, for
 | 
						||
instance when sending data from a form to a server. In the example below there
 | 
						||
is quite a lot going on, and most of it is independent of the actual UI-cycle of
 | 
						||
the component.
 | 
						||
 | 
						||
```jsx
 | 
						||
const handleSubmit = useCallback(
 | 
						||
  async (formValues) => {
 | 
						||
    setPendSaveConfig(true);
 | 
						||
    const payload = new GenerateConfig({
 | 
						||
      workflowId: project_id,
 | 
						||
      blockId: blockId,
 | 
						||
      config: formValues,
 | 
						||
    });
 | 
						||
    axios
 | 
						||
      .post(`${process.env.REACT_APP_ENDPOINT}/save-block-config`, payload)
 | 
						||
      .then((res) => console.log(res))
 | 
						||
      .finally(() => setPendSaveConfig(false))
 | 
						||
      .catch((err) => console.error(err));
 | 
						||
  },
 | 
						||
  [blockId, project_id]
 | 
						||
);
 | 
						||
```
 | 
						||
 | 
						||
Note that the syntax is similar to [React_useEffect](React_useEffect.md): there is a
 | 
						||
dependency array. The effect is the same: the function contained within
 | 
						||
`useCallback` will only re-rendered if one of these dependencies changes.
 | 
						||
However (see next section) the function will run in its memoized form on every
 | 
						||
click.
 | 
						||
 | 
						||
### Reference versus result
 | 
						||
 | 
						||
`useCallback` only memoizes the function _object_ (the reference) not the value
 | 
						||
that is _returned_ by the function (the result).
 | 
						||
 | 
						||
In the example below, the `calculatePi()` function reference will not change
 | 
						||
between renders but each time the click event fires, the value returned by
 | 
						||
`calculatePi()` will change. In other words, it will be assigned fresh memory.
 | 
						||
 | 
						||
```jsx
 | 
						||
function ParentComponent() {
 | 
						||
  const onHandleClick = useCallback(() => {
 | 
						||
    const special = calculatePi();
 | 
						||
  });
 | 
						||
 | 
						||
  return <SubComponent handleClick={onHandleClick} />;
 | 
						||
}
 | 
						||
```
 | 
						||
 | 
						||
### Use cases
 | 
						||
 | 
						||
You should not apply `useCallback` in a blanket fashion, this can reduce
 | 
						||
performance rather than improve it. The best scenarios are:
 | 
						||
 | 
						||
1. A functional component wrapped inside React.memo() accepts a function object
 | 
						||
   prop
 | 
						||
 | 
						||
2. When the function object is a dependency to other hooks, e.g.
 | 
						||
   `useEffect(..., [callback])`
 | 
						||
 | 
						||
3. When the function has some internal state, e.g. when the function is
 | 
						||
   debounced or throttled.
 | 
						||
 | 
						||
## useMemo
 | 
						||
 | 
						||
We can think of `useMemo` as a complement to `useCallback`. The main difference
 | 
						||
is that whilst `useCallback` memoizes the function reference, `useMemo` memoizes
 | 
						||
the function result; the value that is returned.
 | 
						||
 | 
						||
> In memoization, the result is “remembered” when the same parameters are
 | 
						||
> passed-in subsequently. If we have a function compute 1 + 1, it will return 2.
 | 
						||
> But if it uses memoization, the next time we run 1’s through the function, it
 | 
						||
> won’t add them up; it will just remember the answer is 2 without executing the
 | 
						||
> adding function.
 | 
						||
 | 
						||
Like `useCallback`, `useMemo` takes a function and an array of dependencies for
 | 
						||
the same reason: if one of the dependencies changes then the function will
 | 
						||
recalculate (and re-memoize) the result but they don't the same memoized value
 | 
						||
will be returned.
 | 
						||
 | 
						||
As with `useCallback`, `useMemo` is best applied to complex functions that are
 | 
						||
computationally expensive, it shouldn't be used indiscriminately.
 | 
						||
 | 
						||
It should not be used with asynchronous functions, in these instances,
 | 
						||
`useEffect` would be a better choice.
 | 
						||
 | 
						||
### Syntax
 | 
						||
 | 
						||
```jsx
 | 
						||
const List = React.useMemo(
 | 
						||
  () =>
 | 
						||
    listOfItems.map((item) => ({
 | 
						||
      ...item,
 | 
						||
      itemProp1: expensiveFunction(props.first),
 | 
						||
      itemProp2: anotherPriceyFunction(props.second),
 | 
						||
    })),
 | 
						||
  [listOfItems]
 | 
						||
);
 | 
						||
```
 |