React Performance Optimization: Stop Overusing useMemo and useCallback
React
Back to Articles

React Performance Optimization: Stop Overusing useMemo and useCallback

A technical deep dive into when to use React memoization hooks, and why wrapping every function in useCallback is actually making your app slower.

Suleman Zaheer

Suleman Zaheer|سلیمان ظہیر

Full Stack Developer · UET Lahore, Pakistan

May 8, 20254 min read

The Memoization Cargo Cult

A pattern I see constantly in junior and mid-level React codebases: useCallback wrapped around every event handler, useMemo applied to every computed value, React.memo decorating every component. The developer's intent is performance optimization. The actual result is a codebase with more complexity, harder-to-read code, and — paradoxically — often worse performance than the naive version.

This happens because of a fundamental misunderstanding of what memoization costs. On every render, React must allocate memory for the dependencies array, compare each dependency to its previous value using Object.is, and either return the cached value or recompute it. If the savings from avoiding a recomputation are smaller than the overhead of performing these checks, memoization makes things slower.

Understanding When React Re-Renders

A component re-renders in three situations: its own state changes, its parent re-renders (the most common cause of unnecessary re-renders), or a context it subscribes to changes. When a parent component re-renders, every function defined inside the component is recreated as a new object reference. This is why child components wrapped in React.memo still re-render when their props are functions — the prop passes a reference that changes on every parent render.

useCallback: The Two Cases Where It Actually Helps

Case 1: Prop to a memoized child component. If you are passing a function as a prop to a child component wrapped in React.memo, you need useCallback to stabilize the function reference. Without it, the memo wrapper is useless.

Case 2: useEffect dependency. If a function is listed as a dependency in a useEffect, and that function is recreated on every render, the effect runs on every render — often causing infinite loops. useCallback stabilizes the function reference.

In all other situations — click handlers on HTML elements, event handlers not passed to memoized children — useCallback adds cost and complexity without benefit.

useMemo: Expensive Computations Only

useMemo is designed for computationally expensive operations where recalculating on every render is measurably slower than the memoization overhead. For most frontend operations — filtering arrays of 20 items, formatting a date, concatenating strings — the answer is definitively: do not memoize.

// Appropriate — filtering 10,000 products is expensive
const filteredProducts = useMemo(() => {
  return allProducts.filter(p => p.category === selectedCategory && p.price <= maxPrice);
}, [allProducts, selectedCategory, maxPrice]);

// Wasteful — formatting a name is essentially free
const displayName = useMemo(() => `${firstName} ${lastName}`, [firstName, lastName]);

The Real Solution: State Colocation

In my experience, the root cause of React performance problems is almost never missing memoization — it is state stored too high in the component tree. Moving state down to the component that actually needs it eliminates the re-render cascade completely, with no memoization required.

Conclusion

Write clean, simple React code first. Measure performance using the React Profiler when users report slowness. Apply useCallback only when stabilizing function references for memoized child components or useEffect dependencies. Apply useMemo only for genuinely expensive computations verified by profiling. In 90% of React performance problems, the correct solution is better state architecture, not more memoization.

Topics Covered

ReactPerformanceHooksJavaScriptFrontendWeb Development

Found this helpful? Share it.

Suleman Zaheer

Written by

Suleman Zaheer | سلیمان ظہیر

Full Stack Developer from Lahore, Pakistan. CS student at UET Lahore. Building production-grade web applications with MERN Stack and Next.js, and writing about real-world software engineering.

Explore More Articles

9 in-depth technical articles on MERN Stack, Next.js, freelancing, UI/UX, and modern backend architecture.

View All Articles