## 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)