fix(settings): restore mounted flag on remount so auto-save can settle (MUL-4962) - #5613
Conversation
|
@yyclaw is attempting to deploy a commit to the IndexLabs Team on Vercel. A member of the Team first needs to authorize it. |
The auto-save mount effect only cleared `mountedRef` in its cleanup and never re-set it to true on setup. Under React StrictMode the initial setup/cleanup/setup cycle therefore leaves `mountedRef.current` pinned to false for the component's whole life. A successful save then never reaches the terminal branch (`succeeded && mountedRef.current`), so the status is stranded on "saving" and the success toast never fires — the settings save indicator spins forever even though the PATCH returned 200. Set `mountedRef.current = true` on every mount so the flag reflects the live component. Shared by all auto-saved settings (repositories, GitHub, etc.). Add a StrictMode regression test asserting onSuccess still fires.
2cec5df to
993c0be
Compare
|
Thanks for the great fix, @yyclaw! 🙏 The root-cause analysis was spot on: Merging now. Thanks again for the clear write-up and for catching this — it improves every auto-saved settings surface. 🎉 Minor non-blocking follow-up (no action needed): the |
multica-ai#5613) The auto-save mount effect only cleared `mountedRef` in its cleanup and never re-set it to true on setup. Under React StrictMode the initial setup/cleanup/setup cycle therefore leaves `mountedRef.current` pinned to false for the component's whole life. A successful save then never reaches the terminal branch (`succeeded && mountedRef.current`), so the status is stranded on "saving" and the success toast never fires — the settings save indicator spins forever even though the PATCH returned 200. Set `mountedRef.current = true` on every mount so the flag reflects the live component. Shared by all auto-saved settings (repositories, GitHub, etc.). Add a StrictMode regression test asserting onSuccess still fires.
What does this PR do?
Fixes the shared settings auto-save indicator getting stranded on "Saving…" forever, even though the save succeeded.
The
useAutoSavemount effect (packages/views/settings/components/use-auto-save.ts) only clearedmountedRefin its cleanup and never re-set it totrueon setup. Under React StrictMode (the Next.js dev default), the initial setup → cleanup → setup cycle leavesmountedRef.currentpinned tofalsefor the component's whole life. A successful save then never reaches its terminal branch (succeeded && mountedRef.current), sosetStatus("saved")andonSuccessnever fire — the indicator spins forever and no success toast appears, even thoughPATCH /api/workspaces/{id}returned 200.The fix restores the invariant by setting
mountedRef.current = trueon every mount. This is the standard mounted-ref pattern and keeps the flag reflecting the live component across StrictMode's simulated remount (and any genuine remount).Shared by all auto-saved settings surfaces (Repositories, GitHub, etc.), so this fixes the whole set.
Related Issue
Closes #5612
Type of Change
Changes Made
packages/views/settings/components/use-auto-save.ts: setmountedRef.current = truein the mount effect's setup so StrictMode's setup/cleanup/setup cycle no longer strands the flag atfalse.packages/views/settings/components/use-auto-save.test.tsx: add a StrictMode regression test asserting a successful save still transitions to success (onSuccessfires).How to Test
PATCH /api/workspaces/{id}returns 200 but the indicator stays on "Saving…" indefinitely.pnpm --filter @multica/views test -- use-auto-save. The added StrictMode test fails on the old code and passes with the fix.Checklist
AI Disclosure
AI tool used: Claude Code
Prompt / approach: Started from a user-reported "Saving… spins forever" bug with a screenshot. Traced the auto-save state machine end to end, confirmed via DevTools that the PATCH returned 200 (ruling out a hung request), then pinpointed the broken mounted-ref pattern that only misfires under StrictMode's double-invoked mount. Wrote a failing StrictMode regression test first, verified it fails on the old code and passes with the one-line invariant fix.