DEV Community

Cover image for Fixing Unwanted Re-renders in a Nested Component Tree Using React Context
Nitin N.
Nitin N.

Posted on

Fixing Unwanted Re-renders in a Nested Component Tree Using React Context

*Not all React performance issues show up as errors.
*

Some appear quietly during development, when a simple UI interaction feels heavier than it should.

This article walks through a real dev-time issue caused by prop drilling and poor state placement — and how restructuring state fixed unnecessary re-renders, API calls, and heavy UI updates.


This was a React application where a parent component handled:

  • Fetching data from an API
  • Rendering a Highcharts-based visualization (expensive to re-render)

Deep in the component tree (2–3 levels down), there was a child component with a button.

Requirement:

When the button is clicked, show a modal.

Simple on paper.


The Initial Implementation

The modal state was defined in the parent component.

const Parent = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  useEffect(() => {
    fetchData();
  }, []);

  return <Child setIsModalOpen={setIsModalOpen} />;
};
Enter fullscreen mode Exit fullscreen mode

The setIsModalOpen function was passed down multiple levels via props until it reached the button.

<button onClick={() => setIsModalOpen(true)}>
  Open modal
</button>
Enter fullscreen mode Exit fullscreen mode

Functionally, this worked.

Architecturally, it was problematic.


What I Observed During Testing

While casually testing the feature in development, I noticed:

  • A visible UI pause on button click
  • Highcharts re-rendering
  • API calls being triggered again

Every click caused a parent state update, which meant:

  • Parent component re-rendered
  • Side effects tied to the parent executed again
  • Expensive UI re-initialized unnecessarily

Nothing was broken — but the cost was real.


The Root Cause

The issue was state placement.

  • The modal state was UI-only.
  • It lived in a parent component responsible for data fetching and heavy rendering.
  • Prop drilling tightly coupled a local interaction to global side effects
  • This is a common architectural leak in React applications as they grow.

The Fix: Isolate UI State with Context

The solution was to remove modal state from the parent entirely and introduce a dedicated Context for modal visibility.

const ModalContext = createContext(null);

export const ModalProvider = ({ children }) => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <ModalContext.Provider value={{ isOpen, setIsOpen }}>
      {children}
    </ModalContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

The provider was placed at the top-most boundary where modal state made sense.

Components that needed to open or close the modal consumed the context directly.

const { setIsOpen } = useContext(ModalContext);
Enter fullscreen mode Exit fullscreen mode

No prop drilling. No parent involvement.


The Result

After this change:

  • Parent components no longer re-rendered on modal open
  • API calls executed only when explicitly intended
  • Highcharts remained stable
  • UI interactions felt immediate and predictable

The behavior was the same for users, but the architecture was cleaner and more performant.


Key Takeaways

  • Prop drilling can hide performance issues, even when code is correct
  • UI-only state should not live alongside data-fetching logic
  • If a button click triggers unrelated side effects, state boundaries are wrong
  • Context API is not just for global data — it is effective for isolating UI concerns

*Performance issues in React are often design issues in disguise.
*

Top comments (0)