## Notes
- `useMemo` and `useCallback` reduce the amount of work that needs to be done in a given render
- Reduces the number of time that a component needs to re-render
## Use Case 1 - Heavy Computations
Example: calculating prime numbers between 0 and `selectedNum`, where `selectedNum` is a user-supplied value.
```javascript
const allPrimes = React.useMemo(() => {
const result = [];
for (let counter = 2; counter < selectedNum; counter++) {
if (isPrime(counter)) {
result.push(counter);
}
}
return result;
}, [selectedNum]);
```
By wrapping the function in `useMemo`, we tell React to re-render only when its dependencies change.
### Avoiding useMemo
It's possible to avoid `useMemo` in the example provided by Josh Comeau. He created a complex component that does two things:
- Deals with primes
- Displays a date
By splitting out the complex component into two separate components that each do one thing, we can avoid using `useMemo` to reduce the number of re-renders.
## Use Case 2 - Preserved References
```jsx
function App() {
const [name, setName] = React.useState('');
const [boxWidth, setBoxWidth] = React.useState(1);
const id = React.useId();
const boxes = [
{
flex: boxWidth,
background: 'hsl(345deg 100% 50%)'
},
{
flex: 3,
background: 'hsl(260 100% 40%)'
},
{
flex: 1,
background: 'hsl(50deg 100% 60%)'
},
];
return (
<>
<Boxes boxes={boxes} />
<section>
<div className="row">
<label htmlFor={`${id}-name`}>
Name:
</label>
<input
id={`${id}-name`}
type="text"
value={name}
onChange={(event) => {
setName(event.target.value);
}}
/>
</div>
<label htmlFor={`${id}-box-width`}>
First box width:
</label>
<input
id={`${id}-box-width`}
type="range"
min={1}
max={5}
step={0.01}
value={boxWidth}
onChange={(event) => {
setBoxWidth(
Number(event.target.value)
);
}}
/>
</section>
</>
)
}
export default App;
```
And `Boxes.js`
```jsx
function Boxes({ boxes }) {
console.info('Render Boxes ' + new Date());
return (
<div className="boxes-wrapper">
{
boxes.map((boxStyles index) => (
<div
key={index}
className="box"
style={boxStyles}
/>
))
}
</div>
)
};
export default React.memo(Boxes);
```
Above, `Boxes.js` is a pure component. However, it will still re-render if the user changes their name in the input in the parent component.
However, `boxes` in the parent component gets redeclared every time it re-renders. This new assignment fails the `===` check.
For example, in pure JavaScript
```javascript
function getNumbers() {
return [1, 2, 3];
}
const firstResult = getNumbers();
const secondResult = getNumbers();
console.log(firstResult === secondResult);
```
We can work around the redeclaration by wrapping the `const boxes` with `useMemo` like so
```javascript
const boxes = React.useMemo(() => {
return [
{ flex: boxWidth, background: '...' },
{ flex: 3, background: '...' },
{ flex: 1, background: '...' },
]
}, [boxWidth]);
```
In this case, we *preserve a reference*.
## useCallback
`useCallback` does the same thing for **functions** instead of arrays/objects.
Similar to arrays and objects, functions are compared by reference.
```javascript
const functionOne = function() {
return 5;
}
const functionTwo = function() {
return 5;
}
console.log(functionOne === functionTwo);
```
If a function is defined in a component, it will re-generate on every render.
```jsx
// App.js
function App() {
const [count, setCount] = useState(0);
function handleMegaBoost() {
setCount(
(currentValue) => currentValue + 1234
);
}
return (
<>
Count: {count}
<button
onClick={() => setCount(count + 1)}
>
Click me!
</button>
<MegaBoost
handleClick={handleMegaBoost}
/>
</>
);
}
export default App;
```
And in `MegaBoost.js`
```jsx
function MegaBoost({ handleClick }) {
console.info('Render Boxes ' + new Date());
return (
<button
className="mega-boost-button"
onClick={handleClick}
>
MEGA BOOST!
</button>
)
}
export default React.memo(MegaBoost)
```
Because the `handleMegaBoost` function gets redeclared/reinitialized with each render, it will cause the `MegaBoost` component to render again.
```javascript
const handleMegaBoost = useMemo(() => {
return function() {
setCount((currentValue) => currentValue + 1234)
}
}, [])
```
Or, using `useCallback` syntactic sugar
```javascript
const handleMegaBoost = useCallback(() => {
setCount((currentValue) => currentValue + 1234)
}, []);
```
## In Context and Providers
It is common for `context` to return large objects. This would be a good candidate for memoization.
```jsx
const AuthContext = createContext({});
function AuthProvider({ user, status, forgotPwLink, children }) {
const memoizedValue = useMemo(() => {
return {
user,
status,
forgotPwLink,
};
}, [user, status, forgotPwLink]);
return (
<AuthContext.Provider value={memoizedValue}>
{children}
</AuthContext.Provider>
);
}
```
## References
- [[React - Interview Prep]]
- [Understanding useMemo and useCallback](https://joshwcomeau.com/react/usememo-and-usecallback)