Your React Code is Slow and You Don't Even Know It.

Your React Code is Slow and You Don't Even Know It.

GeokHub

GeokHub

Contributing Writer

5 min read
1.0x

You’ve built a beautiful React application. The components are clean, the state is managed, and the features are all working. But something feels… off. It’s not as snappy as you’d like. A button click has a slight delay, typing in a form feels laggy, or navigating between pages isn’t instantaneous.

You check your network, and it’s fine. The backend API is fast. So, what’s the problem?

The culprit is almost certainly hiding in plain sight: unnecessary re-renders. Your React code is doing more work than it needs to, and you probably don’t have the tools turned on to see it.

The Silent Killer: Unnecessary Re-Renders

At the heart of React is a powerful system called reconciliation. When a component’s state or props change, React re-renders that component and its children to figure out what needs to update in the actual DOM.

The problem arises when components re-render even when nothing has changed for them. Imagine a massive component tree where a state change at the top triggers a re-render cascade through hundreds of child components, even though 95% of them are displaying the exact same data as before.

This is wasted computation. It clogs the main JavaScript thread, causing frames to drop and making your app feel janky. On low-powered devices, this effect is magnified, leading to a poor user experience.

How to Spot the Invisible Problem

You can’t fix what you can’t see. Fortunately, React provides excellent tools to diagnose re-renders.

1. Use the React DevTools Profiler

This is your most powerful weapon. The Profiler in React DevTools records how often your components render and how long they take.

  • Go to the “Profiler” tab.
  • Click the record button, use your app for a few seconds, then stop recording.
  • You’ll see a flame graph visualizing every render. Components that flash a lot are re-rendering frequently. Look for components that are rendering but their props/state haven’t changed.

2. Turn on “Highlight Updates”

In the React DevTools “Components” tab, you can enable an option to “Highlight updates when components render.” This will put a colored border around every component on your page as it re-renders. If you see a flash of color where you didn’t expect it (e.g., the entire page flashes when you type in a single input), you’ve found a problem area.

The Most Common Culprits (And How to Fix Them)

Now that you can see the problem, let’s fix it. Here are the most common causes of unnecessary re-renders and their solutions.

1. Inline Objects and Functions as Props

This is the #1 offender.

// DON'T: A new object/function is created on every render
function MyComponent() {
  const [count, setCount] = useState(0);

  return (
    <ChildComponent 
      style={{ color: 'red' }} // New object every time
      onPress={() => setCount(c => c + 1)} // New function every time
    />
  );
}

Every time MyComponent re-renders, it passes brand new style and onPress props to ChildComponent. Even if the values are the same, the props are different by reference, forcing ChildComponent to re-render.

The Fix:

  • useCallback for Functions: Memoize the function so it remains the same across re-renders unless its dependencies change.
  • useMemo for Objects: Memoize the object or value.
  • Move Constants Outside: For static objects, define them outside the component.
// DO: Memoize with useCallback and useMemo
function MyComponent() {
  const [count, setCount] = useState(0);

  const memoizedStyle = useMemo(() => ({ color: 'red' }), []);
  const memoizedOnPress = useCallback(() => setCount(c => c + 1), []);

  return (
    <ChildComponent 
      style={memoizedStyle}
      onPress={memoizedOnPress}
    />
  );
}

2. State Lifted Too High

If you put all your state in the top-level component (e.g., in a context or the App component), then any change to that state will re-render the entire component tree.

The Fix: Colocate State
Keep state as close as possible to where it’s used. If only a Button and a Counter component need a piece of state, lift the state to their closest common parent, not the root of the app.

3. Misconfigureted Context API

The Context API is fantastic, but it can be a performance trap. When the value of a context changes, every component that subscribes to that context will re-render, even if it only uses a part of the context value that didn’t change.

The Fix:

  • Split Contexts: Instead of one giant context for your app state, create multiple, smaller contexts (e.g., UserContext, ThemeContext, CartContext).
  • Memoize the Context Value: If your context value is an object, memoize it with useMemo to prevent unnecessary changes.
// DO: Memoize the context value
function AppProvider({ children }) {
  const [user, setUser] = useState(null);
  const [preferences, setPreferences] = useState({});

  // This value only changes when `user` or `preferences` changes
  const value = useMemo(() => ({
    user,
    preferences,
    setUser,
    setPreferences
  }), [user, preferences]);

  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
}

4. Forgetting key in Lists

When rendering lists, always provide a stable, unique key prop. Using the array index is a last resort and can cause performance issues and bugs if the list order changes. A missing or bad key forces React to do more work reconciling the list items.

The Nuclear Option: React.memo

If you’ve tried the above and a specific component is still re-rendering unnecessarily, you can reach for React.memo.

React.memo is a Higher-Order Component that memoizes your component. It will only re-render if its props change.

const ExpensiveChildComponent = React.memo(function ExpensiveChildComponent({ data }) {
  // ... component logic
});

Use memo sparingly! Don’t wrap every component. It adds its own overhead and is only beneficial for expensive components that re-render often with the same props. Always measure before and after.

Performance is a Feature

A slow app drives users away. By understanding the root cause of React performance issues—unnecessary re-renders—and using the tools and techniques above, you can transform a sluggish interface into a buttery-smooth experience.

Stop guessing. Open your React DevTools, start the Profiler, and see what your components are really doing. You might be surprised at what you find.

Share this article

Help others discover this content