After enabling <StrictMode> in a React app, an analytics 'page_view' event sent from a mount effect starts firing twice in development. How do you decide whether that is a real defect, and what do you change?
answer
- ask whether a real remount would duplicate it
- development revealed it, did not cause it
- user-caused work belongs in the handler
- dedupe where it survives remounts
- harmless if genuinely idempotent
basics
~20 sAsk whether a genuine unmount and remount would also double the event. It would, so this is a real defect that development merely surfaced early. Fix it by moving the send to the interaction that causes it, or by making the operation deduplicate outside the component.
solid answer
~50 sDo not triage it as "a StrictMode thing". The test is simple: would this happen if the user navigated away and came back? For a mount effect that sends a beacon, yes — so the duplication is real, and StrictMode only moved it from production analytics into your console. From there the decision has two branches. If the event is caused by something the user did, it belongs in that event handler, where it fires once per interaction and never on mount at all. If it genuinely tracks "this view was shown", move deduplication somewhere that survives remounts: a dedupe key on the request, a router-level view tracker that fires per navigation, or a small module-scoped cache keyed by the view identity. What you do not do is add a component-local guard, because a real remount resets it.
go deeper
Know the first move: check whether a genuine remount would fire the event twice as well. If it would, the duplication is real and the effect needs changing rather than silencing.
Explain the split between work caused by a user interaction, which belongs in the event handler, and work caused by the view being shown, which needs deduplication outside the component instance.
Show a triage you could run across a whole app — needs cleanup, belongs in a handler, needs external dedup, genuinely harmless — and say how you would verify a fix by remounting for real rather than by watching StrictMode go quiet.
Own where the guarantee lives: pushing idempotency to the collector or the navigation layer protects you from retries, double submissions and client bugs at once, which a per-component fix never does. Be ready to justify that placement against its cost.
## The triage question When StrictMode makes something happen twice, there is exactly one useful question to ask: **would a genuine unmount and remount cause the same thing?** React remounts components for real all the time — route changes, tab switches, a parent whose `key` changed, a subtree that unmounts and returns. If the answer is yes, StrictMode has not created a development artefact; it has given you an early preview of a production bug. Run that test on a mount-effect analytics beacon and the answer is yes. Navigate to the page, leave, come back, and the event fires again. If that is wrong, it was already wrong before StrictMode was switched on. ## Separating the cases The useful split is by *what causes* the event. **Interaction-caused.** "User clicked Subscribe", "user submitted the form", "user dismissed the banner". These have no business in an effect. Effects synchronize a component with an external system; they fire on mount and on dependency change, neither of which is the thing you want to record. Move the call into the handler: ```jsx function Subscribe() { function handleClick() { track('subscribe_clicked'); // once per click, forever subscribe(); } return <button onClick={handleClick}>Subscribe</button>; } ``` This is the single largest category, and moving the call is a strict improvement independent of StrictMode: it is more accurate (a mount is not a click), and it stops depending on render timing. **View-caused.** "This screen was displayed." Here a mount really is the trigger, so the duplication must be handled somewhere that outlives the component instance. Options, roughly in order of preference: - Fire it from navigation rather than from a component. A router-level listener records one view per navigation event, which is what the metric actually means, and is immune to any component churn under it. - Give the request an idempotency or event key the collector deduplicates on. This is the only option that also protects you from retries, double clicks and network replays. - Keep a module-scoped record of "already sent for this view identity" — module scope survives remounts, unlike a ref — with a deliberate policy for when it resets. **Idempotent-anyway.** Some duplicated operations are genuinely harmless: setting a document title, writing the same value to storage, a `PUT` of identical content. Here the second run changes nothing observable. It is still worth checking that the effect has a cleanup, but there is no defect to fix in the payload itself. ## What not to do The two tempting shortcuts both fail the remount test. A `useRef` guard resets when the component instance is recreated, so the duplicate returns in production. Removing `<StrictMode>` removes the report and keeps the behaviour. Both convert a loud development signal into a quiet production one, which is the wrong direction. A third shortcut worth naming: "it only happens in development, ship it." That is true of the *doubling in your console* and false of the *underlying duplication*, and conflating the two is what the interviewer is listening for. ## The wider habit After you have fixed the analytics case, the same triage applies to everything StrictMode reports across the app. Sort each finding into: needs cleanup (subscriptions, timers, listeners, observers), belongs in an event handler (user-caused writes), needs external deduplication (non-idempotent mount work), or genuinely harmless. That classification, not the individual patch, is the senior-level answer — it turns the StrictMode rollout into a bounded, reviewable cleanup rather than an open-ended annoyance. ## Verifying the fix Check it the way the bug arrived: mount, unmount, mount again — navigate away and back with the network panel open — and confirm exactly one event per intended occurrence. If the fix only holds because of StrictMode's specific timing, it is not a fix. And confirm on the collector side, since duplicate suppression there is the layer that survives everything, including the retries you do not control.
- A teammate says the doubling is fine because it only happens in development. How do you respond?The doubling in the console is development-only; the duplication is not. A mount effect that sends a beacon sends it again every time the user navigates away and back, which is a production path StrictMode is merely simulating. Verify it by unmounting and remounting the route for real and watching the network panel — the second event is there without StrictMode involved.
- Where would you put the deduplication so that it survives a real remount?Outside the component instance. Best is the collector side — an event or idempotency key it drops duplicates on, which also covers retries and double submissions you do not control. Next best is the layer that owns the semantics, such as a router-level view tracker firing once per navigation. A module-scoped cache works but needs an explicit reset policy.
- Which duplicated mount effects would you accept without changing anything?Ones whose second run has no observable consequence: setting `document.title`, writing an identical value to storage, updating a ref, an idempotent `PUT` of unchanged content. Even there I check that the effect cleans up after itself, because harmlessness of the payload says nothing about resources the setup acquired.
saying these in an interview costs you the question
- Dismisses it as a development-only artefact
- Adds a ref guard so the second send is skipped
- Removes StrictMode from the tree to stop the noise
- Keeps a click-driven event in a mount effect
- Never asks whether a real remount would duplicate it