Fixing Unwanted Re-renders in a Nested Component Tree Using React Context
Source: Dev.to

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.
The application used React and rendered 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 ;
};
setIsModalOpen was passed down multiple levels via props until it reached the button.
setIsModalOpen(true)}>
Open modal
Functionally, this worked, but 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
Remove modal state from the parent entirely and introduce a dedicated Context for modal visibility.
import { createContext, useState } from "react";
const ModalContext = createContext(null);
export const ModalProvider = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<ModalContext.Provider value={{ isOpen, setIsOpen }}>
{children}
</ModalContext.Provider>
);
};
Place the provider at the top‑most boundary where modal state makes sense. Components that need to open or close the modal consume the context directly:
import { useContext } from "react";
const { setIsOpen } = useContext(ModalContext);
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.
- The 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.