Posted on: 04/09/2026(updated)
Re-renders are crucial to understanding performance in React: how they are triggered, how they propagate through the app, what happens when a component re-renders, and why.
Every re-render starts with state. When we click the button below, we trigger the setIsOpen state setter and update isOpen from false to true. As a result, the App component that holds this state re-renders.
After the state updates and App re-renders, React re-renders its child components to reconcile the UI. In this setup, the BunchOfStuff component re-renders alongside App whenever the state changes—even though it does not depend on isOpen —before the dialog appears on the screen.
import { useState, useEffect } from "react";
import { ModalDialog } from "./components/modal-dialog";
import { Button } from "./components/button";
import { BunchOfStuff } from "./components/mocks";
import "./styles.css";
export default function App() {
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
console.info("Component re-renders");
});
return (
<>
<Button onClick={() => setIsOpen(true)}>
Open dialog
</Button>
{
isOpen ? <ModalDialog onClose={() =>
setIsOpen(false)}
/> : null
}
<BunchOfStuff />
</>
);
}
In React, whether a component's props change or not only prevents a re-render if that component is wrapped in the React.memo higher-order component. While you could wrap components in React.memo and use useCallback to optimise, it is often unnecessary overhead.
Notice that only the Button and ModalDialog components care about isOpen. Instead of relying on memoisation, we can extract those components and the state into a isolated component:
import { useState } from "react";
import { ModalDialog } from "./components/modal-dialog";
import { Button } from "./components/button";
const ButtonWithModalDialog = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)}>
Open dialog
</Button>
{
isOpen ?
<ModalDialog onClose={() =>
setIsOpen(false)}
/>
: null
}
</>
);
};
Then, render this new component inside App:
const App = () => {
return (
<div className="layout">
<ButtonWithModalDialog />
<BunchOfStuff />
</div>
);
};
Now, clicking the button still triggers a state update, but re-renders are localised to ButtonWithModalDialog. BunchOfStuff remains completely untouched.