|
| 1 | +# React.memo and useMemo |
| 2 | + |
| 3 | +## React.memo |
| 4 | + |
| 5 | +`React.memo` is a higher-order component (HOC) that optimizes the performance of functional components by preventing unnecessary re-renders. It works by memoizing the component and only re-renders it when its props change. |
| 6 | + |
| 7 | +### Why Use `React.memo`? |
| 8 | + |
| 9 | +In React, when a parent component re-renders, all its child components also re-render by default, even if their props haven't changed. This can lead to performance issues in large applications or components that are expensive to re-render. |
| 10 | + |
| 11 | +`React.memo` prevents these unnecessary re-renders by memoizing the component and only allowing it to re-render when its props actually change. |
| 12 | + |
| 13 | +### Example Without `React.memo` |
| 14 | + |
| 15 | +`typescript` |
| 16 | + |
| 17 | +```bash |
| 18 | +import React from 'react'; |
| 19 | + |
| 20 | +interface MyComponentProps { |
| 21 | + name: string; |
| 22 | +} |
| 23 | + |
| 24 | +const MyComponent: React.FC<MyComponentProps> = ({ name }) => { |
| 25 | + console.log('Component rendered'); |
| 26 | + return <div>Hello, {name}!</div>; |
| 27 | +}; |
| 28 | + |
| 29 | +const ParentComponent: React.FC = () => { |
| 30 | + const [count, setCount] = React.useState<number>(0); |
| 31 | + |
| 32 | + return ( |
| 33 | + <div> |
| 34 | + <button onClick={() => setCount(count + 1)}>Increment</button> |
| 35 | + <MyComponent name="John" /> |
| 36 | + </div> |
| 37 | + ); |
| 38 | +}; |
| 39 | + |
| 40 | +export default ParentComponent; |
| 41 | +``` |
| 42 | + |
| 43 | +In this example, every time the button is clicked and the `count` state changes, `MyComponent` re-renders even though its `name` prop hasn't changed. |
| 44 | + |
| 45 | +### Example With `React.memo` |
| 46 | + |
| 47 | +`typescript` |
| 48 | + |
| 49 | +```bash |
| 50 | +import React from 'react'; |
| 51 | + |
| 52 | +interface MyComponentProps { |
| 53 | + name: string; |
| 54 | +} |
| 55 | + |
| 56 | +const MyComponent: React.FC<MyComponentProps> = React.memo(({ name }) => { |
| 57 | + console.log('Component rendered'); |
| 58 | + return <div>Hello, {name}!</div>; |
| 59 | +}); |
| 60 | +
|
| 61 | +const ParentComponent: React.FC = () => { |
| 62 | + const [count, setCount] = React.useState<number>(0); |
| 63 | +
|
| 64 | + return ( |
| 65 | + <div> |
| 66 | + <button onClick={() => setCount(count + 1)}>Increment</button> |
| 67 | + <MyComponent name="John" /> |
| 68 | + </div> |
| 69 | + ); |
| 70 | +}; |
| 71 | +
|
| 72 | +export default ParentComponent; |
| 73 | +
|
| 74 | +``` |
| 75 | +
|
| 76 | +By using `React.memo`, `MyComponent` only re-renders if its `name` prop changes, improving performance by avoiding unnecessary re-renders. |
| 77 | +
|
| 78 | +## How `React.memo` Works |
| 79 | +
|
| 80 | +`React.memo` performs a shallow comparison of the component's props. If the props are the same as the previous render, the component will not re-render. |
| 81 | +
|
| 82 | +Primitives (numbers, strings, booleans) are compared by value. |
| 83 | +Objects, arrays, and functions are compared by reference. |
| 84 | +Custom Comparison Function |
| 85 | +If you need more control over how the props are compared, you can pass a custom comparison function as the second argument to `React.memo`. |
| 86 | +
|
| 87 | +### Example With `Custom Comparison` |
| 88 | +
|
| 89 | +`typescript` |
| 90 | +
|
| 91 | +```bash |
| 92 | +import React from 'react'; |
| 93 | +
|
| 94 | +interface MyComponentProps { |
| 95 | + name: string; |
| 96 | + info: { |
| 97 | + age: number; |
| 98 | + }; |
| 99 | +} |
| 100 | +
|
| 101 | +const MyComponent: React.FC<MyComponentProps> = React.memo( |
| 102 | + ({ name, info }) => { |
| 103 | + console.log('Component rendered'); |
| 104 | + return ( |
| 105 | + <div> |
| 106 | + <p>Hello, {name}!</p> |
| 107 | + <p>Age: {info.age}</p> |
| 108 | + </div> |
| 109 | + ); |
| 110 | + }, |
| 111 | + (prevProps, nextProps) => { |
| 112 | + return prevProps.name === nextProps.name && prevProps.info.age === nextProps.info.age; |
| 113 | + } |
| 114 | +); |
| 115 | +
|
| 116 | +const ParentComponent: React.FC = () => { |
| 117 | + const [count, setCount] = React.useState<number>(0); |
| 118 | + const info = { age: 25 }; |
| 119 | +
|
| 120 | + return ( |
| 121 | + <div> |
| 122 | + <button onClick={() => setCount(count + 1)}>Increment</button> |
| 123 | + <MyComponent name="John" info={info} /> |
| 124 | + </div> |
| 125 | + ); |
| 126 | +}; |
| 127 | +
|
| 128 | +export default ParentComponent; |
| 129 | +``` |
| 130 | +
|
| 131 | +Here, the custom comparison function ensures `MyComponent` only re-renders when the `name` or `info.age` changes. |
| 132 | +
|
| 133 | +## useMemo |
| 134 | +
|
| 135 | +`useMemo` is a React hook that optimizes performance by memoizing the result of a calculation or a function call. It recomputes the value only when one of its dependencies changes. |
| 136 | +
|
| 137 | +### Why Use useMemo? |
| 138 | +
|
| 139 | +Without `useMemo`, expensive calculations inside a component will be recalculated every time the component renders, even if the inputs to the calculation haven't changed. This can lead to performance issues. |
| 140 | +
|
| 141 | +By using `useMemo`, you can memoize the result of the calculation and avoid unnecessary recalculations. |
| 142 | +
|
| 143 | +### Example Without `useMemo` |
| 144 | +
|
| 145 | +`typescript` |
| 146 | +
|
| 147 | +```bash |
| 148 | +import React from 'react'; |
| 149 | +
|
| 150 | +const calculateValue = (num: number): number => { |
| 151 | + console.log('Expensive calculation'); |
| 152 | + return num * 2; |
| 153 | +}; |
| 154 | +
|
| 155 | +interface MyComponentProps { |
| 156 | + num: number; |
| 157 | +} |
| 158 | +
|
| 159 | +const MyComponent: React.FC<MyComponentProps> = ({ num }) => { |
| 160 | + const value = calculateValue(num); |
| 161 | +
|
| 162 | + return <div>Value: {value}</div>; |
| 163 | +}; |
| 164 | +
|
| 165 | +const ParentComponent: React.FC = () => { |
| 166 | + const [count, setCount] = React.useState<number>(0); |
| 167 | +
|
| 168 | + return ( |
| 169 | + <div> |
| 170 | + <button onClick={() => setCount(count + 1)}>Increment</button> |
| 171 | + <MyComponent num={5} /> |
| 172 | + </div> |
| 173 | + ); |
| 174 | +}; |
| 175 | +
|
| 176 | +export default ParentComponent; |
| 177 | +``` |
| 178 | +
|
| 179 | +In this example, `calculateValue` is called every time `ParentComponent` renders, even though `num` remains the same. |
| 180 | +
|
| 181 | +### Example Without `useMemo` |
| 182 | +
|
| 183 | +`typescript` |
| 184 | +
|
| 185 | +```bash |
| 186 | +import React from 'react'; |
| 187 | +
|
| 188 | +const calculateValue = (num: number): number => { |
| 189 | + console.log('Expensive calculation'); |
| 190 | + return num * 2; |
| 191 | +}; |
| 192 | +
|
| 193 | +interface MyComponentProps { |
| 194 | + num: number; |
| 195 | +} |
| 196 | +
|
| 197 | +const MyComponent: React.FC<MyComponentProps> = ({ num }) => { |
| 198 | + const value = React.useMemo(() => calculateValue(num), [num]); |
| 199 | +
|
| 200 | + return <div>Value: {value}</div>; |
| 201 | +}; |
| 202 | +
|
| 203 | +const ParentComponent: React.FC = () => { |
| 204 | + const [count, setCount] = React.useState<number>(0); |
| 205 | +
|
| 206 | + return ( |
| 207 | + <div> |
| 208 | + <button onClick={() => setCount(count + 1)}>Increment</button> |
| 209 | + <MyComponent num={5} /> |
| 210 | + </div> |
| 211 | + ); |
| 212 | +}; |
| 213 | +
|
| 214 | +export default ParentComponent; |
| 215 | +``` |
| 216 | +
|
| 217 | +In this example, `calculateValue` is only called when `num` changes, thanks to `useMemo`. |
| 218 | +
|
| 219 | +## How useMemo Works |
| 220 | +
|
| 221 | +useMemo memoizes the result of the function passed to it. It takes two arguments: |
| 222 | +
|
| 223 | +1. A function that returns the value you want to memoize. |
| 224 | +2. A dependency array. The function is only recomputed when one of the dependencies changes. |
| 225 | +
|
| 226 | +### Example With `Dependency Array` |
| 227 | +
|
| 228 | +`typescript` |
| 229 | +
|
| 230 | +```bash |
| 231 | +import React from 'react'; |
| 232 | +
|
| 233 | +const calculateValue = (num: number, multiplier: number): number => { |
| 234 | + console.log('Expensive calculation'); |
| 235 | + return num * multiplier; |
| 236 | +}; |
| 237 | +
|
| 238 | +const MyComponent: React.FC = () => { |
| 239 | + const [num, setNum] = React.useState<number>(5); |
| 240 | + const [multiplier, setMultiplier] = React.useState<number>(2); |
| 241 | +
|
| 242 | + const value = React.useMemo(() => calculateValue(num, multiplier), [num, multiplier]); |
| 243 | +
|
| 244 | + return ( |
| 245 | + <div> |
| 246 | + <button onClick={() => setNum(num + 1)}>Increment Num</button> |
| 247 | + <button onClick={() => setMultiplier(multiplier + 1)}>Increment Multiplier</button> |
| 248 | + <div>Value: {value}</div> |
| 249 | + </div> |
| 250 | + ); |
| 251 | +}; |
| 252 | +
|
| 253 | +export default MyComponent; |
| 254 | +``` |
| 255 | +
|
| 256 | +In this example, `calculateValue` is only called when either `num` or `multiplier changes`. |
| 257 | +
|
| 258 | +## Conclusion |
| 259 | +
|
| 260 | +Both `React.memo` and `useMemo` are useful performance optimization tools in React. |
| 261 | +
|
| 262 | +- Use `React.memo` to prevent unnecessary re-renders of child components by memoizing them. |
| 263 | +- Use useMemo to memoize expensive calculations or function results to avoid recalculating on every render. |
| 264 | +
|
| 265 | +## References |
| 266 | +
|
| 267 | +- React.memo Documentation (https://reactjs.org/docs/react-api.html#reactmemo) |
| 268 | +- useMemo Documentation (https://reactjs.org/docs/hooks-reference.html#usememo) |
| 269 | +
|
| 270 | +## Additional Resources |
| 271 | +
|
| 272 | +### Medium Articles: |
| 273 | +
|
| 274 | +- Memoization in React (https://medium.com/@rumeysakaragoz18/memoization-in-react-memo-usecallback-and-usememo-69b1367041e0) |
| 275 | +- React Performance Optimization using React.memo or useMemo (https://medium.com/@arunsheoran90/react-performance-optimisation-using-react-memo-usememo-6edcb25a1ed6) |
| 276 | +
|
| 277 | +### Walkthrough Videos: |
| 278 | +
|
| 279 | +- Learn useMemo and React Memo for Faster Apps: (https://www.youtube.com/watch?v=6-BfMpTT2PE). |
| 280 | +
|
| 281 | +- useMemo Explained | React Hooks useMemo Tutorial: (https://www.youtube.com/watch?v=oR8gUi1LfWY) |
| 282 | +
|
| 283 | +- Mastering React Memo:( https://www.youtube.com/watch?v=DEPwA3mv_R8) |
| 284 | +
|
| 285 | +- Learn useMemo In 15 Minutes - React Hooks Explained: (https://www.youtube.com/watch?v=JvdxYsF66K4) |
| 286 | +
|
| 287 | +- React.memo, useMemo, and useCallback Optimizations: (https://www.youtube.com/watch?v=4BranN3qnDU) |
0 commit comments