- Регистрация
- 1 Мар 2015
- Сообщения
- 1,481
- Баллы
- 155
Mastering useMemo and useCallback in React: A Deep Dive
In modern React development, performance and precision matter more than ever. Two powerful hooks, useMemo and useCallback, empower developers to optimize rendering behavior, avoid unnecessary recalculations, and manage referential integrity.
In this article, we will explore these two hooks through an advanced real-world example: simulating an expensive math operation in a component and preventing unnecessary renders. We'll cover their use cases, differences, similarities, and best practices to use them effectively.
What Are useMemo and useCallback?
useMemo
- Purpose: Memoizes the result of a computation.
- When to use: When an expensive calculation should be recomputed only when its dependencies change.
- Returns: A memoized value.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback
- Purpose: Memoizes the function reference itself.
- When to use: When a function is passed to child components or included in dependencies and you want to avoid re-creating it on every render.
- Returns: A memoized function.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Real-World Example: Fibonacci + Expensive Render Simulation
We'll use both hooks to:
- Optimize a simulated heavy calculation (Fibonacci).
- Avoid recreating functions using useCallback.
import React, { useState, useMemo, useCallback } from 'react';
const fibonacci = (n: number): number => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
};
const ExpensiveCalculation = ({ number }: { number: number }) => {
const result = useMemo(() => fibonacci(number), [number]);
console.log('? ExpensiveCalculation rendered');
return <p>Fibonacci({number}) = {result}</p>;
};
const Counter = ({ onIncrement }: { onIncrement: () => void }) => {
console.log('? Counter rendered');
return <button onClick={onIncrement}>+1</button>;
};
export const OptimizedComponent = () => {
const [count, setCount] = useState(0);
const [fibNumber, setFibNumber] = useState(20);
const increment = useCallback(() => setCount((prev) => prev + 1), []);
return (
<div>
<h2>useMemo & useCallback Demo</h2>
<Counter onIncrement={increment} />
<p>Clicked: {count}</p>
<input
type="number"
value={fibNumber}
onChange={(e) => setFibNumber(Number(e.target.value))}
/>
<ExpensiveCalculation number={fibNumber} />
</div>
);
};
Why This Works
useMemo Benefits
- Without useMemo, fibonacci(number) would re-run on every render, even when unrelated state like count changes.
- useMemo memoizes the result unless number changes.
- Without useCallback, onIncrement would get a new reference each time, triggering unnecessary re-renders in child components.
- useCallback ensures Counter only re-renders when truly needed.
| Feature | useMemo | useCallback |
|---|---|---|
| Memoizes | Return value of function | Function itself |
| Use case | Avoid recalculating values | Avoid recreating function refs |
| Return type | Any value | A function |
| Helps with | Expensive calculations | Referential equality (e.g. props) |
- Use these hooks only when necessary. Over-optimization may lead to more complexity.
- Ensure dependencies arrays are accurate and complete.
- Prefer useMemo for derived values and useCallback when passing functions to children or in useEffect.
You can integrate why-did-you-render or React DevTools Profiler to visualize how memoization impacts rendering behavior.
Conclusion
React's useMemo and useCallback are powerful hooks that help fine-tune performance and reference stability. Understanding when and how to use them can significantly enhance your app's responsiveness and efficiency.
By combining these hooks, you can:
- Prevent unnecessary re-renders
- Speed up expensive calculations
- Pass stable functions to child components
Mastering these hooks is a major step toward becoming a true React expert. ?
#react #typescript #frontend #performance #memoization #hooks #useMemo #useCallback