Introduction
React is powerful, but it can suffer performance issues when components re-render unnecessarily or handle more work than needed.
This guide explores 6 common performance bottlenecks in real-world applications — along with practical techniques to address them. Each example includes before-and-after code and clear explanations of what’s happening, why it matters, and how to optimize it effectively.
1. Unnecessary Re-renders in Child Components
Consider a simple Counter component that displays a number. Even if only the input field is updated, the Counter still re-renders - a common inefficiency in many apps.
Before Optimization
After Optimization (using react-window)
Every time the
App
component re-renders - even when updating the text
input — the Counter
component also re-renders.Why it's a problem?
React re-renders all child components by default, even if their props haven’t changed. This adds up fast in large trees.
Optimization Approach
Use
React.memo
to prevent unnecessary re-renders by memoizing the component unless its props actually change.2. Rendering Huge Lists Without Virtualization
Before Optimization
After Optimization (using react-window
)
What's happening?
All 10,000 list items are rendered and pushed into the DOM at once.
Why it's a problem?
Rendering thousands of DOM nodes is memory-intensive and causes the browser to lag or freeze.
How to fix it?
Use virtualization (react-window
) to render only visible items and reuse DOM nodes during scroll. Smooth performance, minimal overhead.
3. Function Re-created on Every Render
onClick
handler in a button). This can cause unnecessary re-renders of child components, especially when they are memorized.Before Optimization
After Optimization
Button
component is memoized, it will still re-render because the onClick
prop changes by reference.
useCallback
to keep the reference stable across renders and avoid unnecessary updates downstream.4. Tab Content Re-renders on Every Switch
Before Optimization
After Optimization
What's happening?
Each tab component mounts/unmounts when switching, resetting its internal state and triggering re-renders.
Why it's a problem?
Components lose their internal state, and there's a noticeable delay on tab switches due to remounting.
How to fix it?
Store components in `useMemo` and toggle visibility instead of remounting. This keeps the component’s state and makes switching faster.
5. Uncontrolled Component Switching to Controlled
Before Optimization
value
is initially undefined
, this becomes uncontrolled, and when it gets a value, React throws a warning and forces a re-renderAfter Optimization
What's happening?
If value
starts as undefined
, React treats the input as uncontrolled. When it gets a value later, it switches to controlled — and React throws a warning.
Why it's a problem?
Switching from an uncontrolled to a controlled input in React causes the field to reset and displays a warning.
How to fix it?
Use a fallback value (value ?? ''
) to keep the input controlled from the beginning, avoiding warning and preserving expected behavior.
6. Large Components Doing Too Much
Before Optimization
All components are rendered and mounted at once.
After Optimization (Code Splitting)
Why it's a problem?
It bloats the initial JavaScript bundle, slowing down your app's first render and hurting metrics like LCP.
How to fix it?
Use React.lazy with Suspense to load components only when you need them. This helps the app load faster and feel more responsive.
Final Thoughts
Performance issues in React often creep in silently - small inefficiencies that add up as your app grows. These optimization patterns are widely applicable and can help improve performance and user experience across React applications of all sizes.
No comments:
Post a Comment