Lesson 58 of 80
Fast Refresh is why editing a component keeps its state instead of reloading the page — and there's a specific set of edits where that guarantee quietly breaks.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
When you save a file in a React dev server, the whole page usually doesn't reload. Fast Refresh (React's implementation of Hot Module Replacement for components) swaps in the new component code in place, and — when it can — keeps the existing component instances mounted, with their state intact. Toggle a modal open, edit a line of JSX in that modal's component, save: the modal is still open. That's Fast Refresh working.
It works by re-running the module and comparing the new component function to the old one. If it still looks like "the same component" (same file, same export, compatible enough), React swaps the render function but reuses the existing fiber and its state. If the edit changes something React can't safely reconcile that way, it falls back to unmounting and remounting the component fresh — state gone, same as a page reload for that subtree.
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((c) => c + 1)}>
{/* editing this label and saving: count survives */}
Clicked {count} times
</button>
);
}
Editing that label, a class name, or the JSX structure below the hooks and saving — the button re-renders with the new markup, count keeps whatever value it had. Now compare:
function Counter() {
const [count, setCount] = useState(0);
const [label, setLabel] = useState("times"); // added this line and saved
return (
<button onClick={() => setCount((c) => c + 1)}>
Clicked {count} {label}
</button>
);
}
Adding a second useState call changes the order and shape of hooks for every already-mounted instance of Counter. React can't safely map "the old fiber's hook slot 0" onto a component that now expects two hook slots without risking corrupted state, so Fast Refresh remounts the component instead — count resets to 0.
react-refresh/only-export-components flag mixed exports.The same rule that makes React reuse a fiber across renders — same component type in the same position — is what Fast Refresh leans on across edits, not just re-renders. And the reason adding a hook resets state is the same reason the rules of hooks forbid conditional hook calls in the first place: React tracks hook state by call order, not by name, so any change to that order or count is treated as a fundamentally different component.