|
| 1 | +# Memoization in React with TypeScript |
| 2 | + |
| 3 | +This guide covers two important memoization techniques in React: the `React.memo` higher-order component and the `useMemo` hook. Both are essential for optimizing performance in React applications, especially when used with TypeScript. |
| 4 | + |
| 5 | +## Table of Contents |
| 6 | +1. [React.memo](#reactmemo) |
| 7 | +2. [useMemo Hook](#usememo-hook) |
| 8 | + |
| 9 | +## React.memo |
| 10 | + |
| 11 | +### What is React.memo? |
| 12 | + |
| 13 | +React.memo is a higher-order component (HOC) that memoizes functional components to prevent unnecessary re-renders when props haven't changed. |
| 14 | + |
| 15 | +### When to Use React.memo |
| 16 | + |
| 17 | +Use React.memo when: |
| 18 | +- A component renders often with the same props |
| 19 | +- The component is pure (output determined solely by props) |
| 20 | +- Rendering the component is relatively expensive |
| 21 | + |
| 22 | +### Basic Usage with TypeScript |
| 23 | +`typescript` |
| 24 | +```bash |
| 25 | +import React from 'react'; |
| 26 | +interface GreetingProps { |
| 27 | +name: string; |
| 28 | +} |
| 29 | +const Greeting: React.FC<GreetingProps> = ({ name }) => { |
| 30 | +console.log('Greeting component rendered'); |
| 31 | +return <h1>Hello, {name}!</h1>; |
| 32 | +}; |
| 33 | +export default React.memo(Greeting); |
| 34 | +``` |
| 35 | + |
| 36 | +### Custom Comparison Function |
| 37 | +`typescript` |
| 38 | +```bash |
| 39 | +import React from 'react'; |
| 40 | +interface ComplexProps { |
| 41 | +id: number; |
| 42 | +data: { |
| 43 | +name: string; |
| 44 | +value: number; |
| 45 | +}; |
| 46 | +} |
| 47 | +const ComplexComponent: React.FC<ComplexProps> = ({ id, data }) => { |
| 48 | +return ( |
| 49 | +<div> |
| 50 | +<h3>ID: {id}</h3> |
| 51 | +<p>Name: {data.name}</p> |
| 52 | +<p>Value: {data.value}</p> |
| 53 | +</div> |
| 54 | +); |
| 55 | +}; |
| 56 | +function arePropsEqual(prevProps: ComplexProps, nextProps: ComplexProps) { |
| 57 | +return ( |
| 58 | +prevProps.id === nextProps.id && |
| 59 | +prevProps.data.name === nextProps.data.name && |
| 60 | +prevProps.data.value === nextProps.data.value |
| 61 | +); |
| 62 | +} |
| 63 | +export default React.memo(ComplexComponent, arePropsEqual); |
| 64 | +``` |
| 65 | + |
| 66 | +## useMemo Hook |
| 67 | + |
| 68 | +### What is useMemo? |
| 69 | + |
| 70 | +`useMemo` is a React hook that memoizes the result of a computation, recomputing only when its dependencies change. |
| 71 | + |
| 72 | +### When to Use useMemo |
| 73 | + |
| 74 | +Use `useMemo` when: |
| 75 | +- You have computationally expensive operations |
| 76 | +- You want to avoid unnecessary re-computations on re-renders |
| 77 | +- You need to maintain referential equality for complex objects |
| 78 | + |
| 79 | +### Basic Usage with TypeScript |
| 80 | +```bash |
| 81 | +import React, { useMemo } from 'react'; |
| 82 | +interface Props { |
| 83 | +numbers: number[]; |
| 84 | +} |
| 85 | +const SumComponent: React.FC<Props> = ({ numbers }) => { |
| 86 | +const sum = useMemo(() => { |
| 87 | +console.log('Calculating sum...'); |
| 88 | +return numbers.reduce((acc, num) => acc + num, 0); |
| 89 | +}, [numbers]); |
| 90 | +return <div>Sum: {sum}</div>; |
| 91 | +}; |
| 92 | +export default SumComponent; |
| 93 | +``` |
| 94 | +
|
| 95 | +
|
| 96 | +### Complex Example: Memoizing Expensive Calculations |
| 97 | +
|
| 98 | +```bash |
| 99 | +import React, { useMemo } from 'react'; |
| 100 | +
|
| 101 | +interface DataPoint { |
| 102 | + x: number; |
| 103 | + y: number; |
| 104 | +} |
| 105 | +
|
| 106 | +interface ComplexCalculationProps { |
| 107 | + data: DataPoint[]; |
| 108 | + threshold: number; |
| 109 | +} |
| 110 | +
|
| 111 | +const ComplexCalculationComponent: React.FC<ComplexCalculationProps> = ({ data, threshold }) => { |
| 112 | + const result = useMemo(() => { |
| 113 | + console.log('Performing complex calculation...'); |
| 114 | + |
| 115 | + // Simulate an expensive calculation |
| 116 | + const filteredData = data.filter(point => point.x > threshold); |
| 117 | + const sum = filteredData.reduce((acc, point) => acc + point.y, 0); |
| 118 | + const average = sum / filteredData.length; |
| 119 | + |
| 120 | + // Simulate more complex operations |
| 121 | + const standardDeviation = Math.sqrt( |
| 122 | + filteredData.reduce((acc, point) => acc + Math.pow(point.y - average, 2), 0) / filteredData.length |
| 123 | + ); |
| 124 | + |
| 125 | + return { |
| 126 | + filteredCount: filteredData.length, |
| 127 | + average: average.toFixed(2), |
| 128 | + standardDeviation: standardDeviation.toFixed(2) |
| 129 | + }; |
| 130 | + }, [data, threshold]); |
| 131 | +
|
| 132 | + return ( |
| 133 | + <div> |
| 134 | + <h3>Complex Calculation Results:</h3> |
| 135 | + <p>Filtered Data Points: {result.filteredCount}</p> |
| 136 | + <p>Average Y Value: {result.average}</p> |
| 137 | + <p>Standard Deviation: {result.standardDeviation}</p> |
| 138 | + </div> |
| 139 | + ); |
| 140 | +}; |
| 141 | +
|
| 142 | +export default ComplexCalculationComponent; |
| 143 | +``` |
| 144 | +
|
| 145 | +
|
| 146 | +## Best Practices for Memoization |
| 147 | +
|
| 148 | +1. Don't overuse: Only apply memoization when there's a clear performance benefit. |
| 149 | +2. Measure performance: Use profiling tools to identify and verify performance improvements. |
| 150 | +3. Avoid premature optimization: Start with simple implementations and optimize as needed. |
| 151 | +4. Be careful with dependencies: Ensure all variables used in memoized functions are included in dependency arrays. |
| 152 | +5. Consider extraction: For complex memoized logic, consider creating custom hooks. |
| 153 | +
|
| 154 | +## Conclusion |
| 155 | +
|
| 156 | +Both `React.memo` and `useMemo` are powerful tools for optimizing React applications, especially when used with TypeScript. They can significantly improve performance when applied judiciously. Always measure the impact of your optimizations and use these techniques when they provide tangible benefits to your application's performance. |
0 commit comments