← All posts
usecallbackreactjsreact-hookusememo

React Performance Optimization: useMemo vs useCallback

Jan 19, 20264 min read

useMemo vs useCallback

Understanding when to cache values and when to cache functions

Introduction

React provides two important hooks for performance optimization: useMemo and useCallback. Both hooks are used for memoization, but they serve different purposes. This blog post will give you a clear understanding of when to use which hook.

What is Memoization?

Memoization is an optimization technique that caches the results of expensive calculations or operations. When the same inputs occur again, the cached result is returned instead of recalculating. This improves performance and reduces unnecessary re-renders.

useMemo: For Caching Values

The useMemo hook caches the result of expensive calculations. It’s used for numbers, arrays, objects, or any computed value.

Syntax

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Use Cases

  • Heavy calculations: When a calculation is computationally expensive
  • Filtered arrays: Filtering or sorting large arrays
  • Derived state: Creating computed values from props
  • Reference stability: Keeping objects or arrays reference-stable

Example

import { useMemo, useState } from 'react';
function ProductList({ products }) {
const [filter, setFilter] = useState('');
  // Cache the expensive filtering operation
const filteredProducts = useMemo(() => {
console.log('Filtering products...');
return products.filter(product =>
product.name.toLowerCase().includes(filter.toLowerCase())
);
}, [products, filter]);
  return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Search products..."
/>
{filteredProducts.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
}

useCallback: For Caching Functions

The useCallback hook caches a function reference. This is useful when you pass functions to child components and want to avoid unnecessary re-renders.

Syntax

const memoizedCallback = useCallback(() => { 
doSomething(a, b);
}, [a, b]);

Use Cases

  • Passing callbacks: When passing callbacks to child components
  • Event handlers: Event handlers with memoized components
  • Dependency arrays: Functions in useEffect or useMemo dependencies
  • Custom hooks: Returning stable function references

Example

import { useCallback, useState, memo } from 'react';
// Wrap child component with memo
const Button = memo(({ onClick, children }) => {
console.log('Button rendered:', children);
return <button onClick={onClick}>{children}</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const [other, setOther] = useState(0);
  // Keep function reference stable
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // Empty dependency - function never changes
  return (
<div>
<p>Count: {count}</p>
<p>Other: {other}</p>
<Button onClick={handleClick}>Increment Count</Button>
<button onClick={() => setOther(o => o + 1)}>
Increment Other
</button>
</div>
);
}

useMemo vs useCallback: Quick Comparison

Aspect useMemo useCallback Purpose Caches values Caches functions Returns Computed value/result Memoized function Use Case Expensive calculations, arrays, objects Event handlers, callbacks to child components Example useMemo(() => a + b, [a, b]) useCallback(() => fn(a), [a])

Key Differences

  1. Return Value: useMemo returns a computed value while useCallback returns a function.
  2. Execution: useMemo executes the function and stores the result. useCallback only stores the function reference without executing it.
  3. Memory: useMemo caches the value (potentially more memory), useCallback only caches the function definition.

Important Note

useCallback(fn, deps) and useMemo(() => fn, deps) are technically the same! useCallback is internally a wrapper around useMemo.

When to Use What?

Use useMemo When:

  • You need to cache the result of an expensive calculation
  • You need to filter/transform large arrays or objects
  • You need to compute derived state
  • You need to maintain reference stability (for objects/arrays)

Use useCallback When:

  • You’re passing callbacks to child components (especially memo components)
  • You need to use a function in a dependency array (useEffect, useMemo, etc.)
  • You want to keep event handlers stable
  • You want to return stable function references from custom hooks

Common Pitfalls to Avoid

  1. Over-optimization: Using useMemo/useCallback everywhere is wrong. Only use them where there’s actual performance benefit.
  2. Missing dependencies: All dependencies must be included in the dependency array, otherwise you’ll get stale closures.
  3. Premature optimization: Write code first, measure it, then optimize. Don’t optimize without profiling.
  4. Complex dependencies: If your dependency array is very complex, you might need to restructure your component.

Best Practices

  • Profile first: Use React DevTools Profiler to identify actual performance issues
  • Use with memo: useCallback’s value is highest when the child component is wrapped with React.memo
  • Keep it simple: If optimization is getting complex, splitting the component might be a better solution
  • ESLint rules: Use eslint-plugin-react-hooks — it automatically catches dependency array issues
  • Document your optimizations: Write comments explaining why you added the optimization — it helps future developers

Conclusion

useMemo and useCallback are powerful optimization tools in React, but it’s important to use them wisely. useMemo caches values (numbers, arrays, objects), while useCallback caches functions.

Remember: Premature optimization is the root of all evil in programming. First write clear and maintainable code, then identify performance issues (through profiling), and only then optimize where it’s actually needed.

Happy coding! 🚀

Written with ❤️ for React developers

Follow for more React tips and tricks! mirbasit01