Conversation
## Because - The /pair bootstrap effect depended on the whole `fxaStatusResult` object. `useFxAStatus` builds a new object literal on every render, so that dependency changed identity on each render. - The effect therefore ran again on every parent render. Each extra pass started a second Sync OAuth flow and a second hard navigation, and Firefox cancelled the first one. ## This pull request - Makes the bootstrap effect depend on the primitive fxa_status fields (`fxaStatusState` and `fxaStatus?.capabilities.pairingVersion`) instead of the result object. - Adds a mount-scoped `bootstrapStartedRef`. The async WebChannel bootstrap now starts one time per mount at most. - Replaces the run-scoped `cancelled` local with `abortBootstrapRef`. Each pass of the effect raises the flag, and the effect lowers it again after the last branch that routes elsewhere. A pass that routes elsewhere therefore stands an in-flight bootstrap down. Unmount raises the flag too. - Adds three tests: a parent re-render, a late fxa_status answer, and a late pairing v2 answer that reaches the QR scanner. Adds a shared `MOCK_OAUTH_PARAMS` constant for the params that the three tests share. ## Issue that this pull request solves Closes: https://mozilla-hub.atlassian.net/browse/FXA-14544
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The lifecycle changes are focused and adequately covered by regression tests.
Pull request overview
Prevents duplicate /pair Sync bootstrap and navigation flows during re-renders or late Firefox status updates.
Changes:
- Uses stable primitive effect dependencies and mount-scoped lifecycle refs.
- Aborts in-flight bootstrap when routing elsewhere or unmounting.
- Adds regression coverage for re-renders and late status responses.
File summaries
| File | Description |
|---|---|
packages/fxa-settings/src/pages/Pair/Index/index.tsx |
Makes bootstrap execution mount-scoped and abortable. |
packages/fxa-settings/src/pages/Pair/Index/index.test.tsx |
Tests duplicate prevention and late-response routing. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
|
@vbudhram I feel like I was also getting some undesirable double render logic in various places too... I'm happy to R+ to unblock the issue. I do wonder if the effect is actually more wide spread, impacting other pages too, and the fix should be directed at the way the useFxaStatus hook works. |
dschom
self-requested a review
September 18, 2026 16:22
dschom
approved these changes
Sep 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Because
fxaStatusResultobject.useFxAStatusbuilds a new object literal on every render, so that dependency changed identity on each render.This pull request
fxaStatusStateandfxaStatus?.capabilities.pairingVersion) instead of the result object.bootstrapStartedRef. The async WebChannel bootstrap now starts one time per mount at most.cancelledlocal withabortBootstrapRef. Each pass of the effect raises the flag, and the effect lowers it again after the last branch that routes elsewhere. A pass that routes elsewhere therefore stands an in-flight bootstrap down. Unmount raises the flag too.MOCK_OAUTH_PARAMSconstant for the params that the three tests share.Issue that this pull request solves
Closes: https://mozilla-hub.atlassian.net/browse/FXA-14544
Checklist
Put an
xin the boxes that applyHow to review (Optional)
Pair/Index/index.tsx, and the two refs above it.index.tsxfirst, then the three new tests inindex.test.tsx.abortBootstrapRef. The effect raises the flag at the top of each pass and lowers it after the last branch that routes elsewhere. A new early return below that point would leave the flag raised and hold the page on the spinner.Screenshots (Optional)
None. The change alters effect dependencies, not rendered output.
Other information (Optional)
Local results:
npx tsc -p packages/fxa-settings/tsconfig.json --noEmit: clean.npx nx lint fxa-settings: exit 0.HEAD, so they pin the regression. I did not run the Playwright functional tests.useFxAStatusstays unmemoized on purpose. The acceptance criteria accept either approach. A memoized return object needsofferedSyncEnginesmemoized first, and that change reaches every consumer of the hook.I also left out the optional functional-test hardening on purpose. The ticket counts five
page.waitForURL(/action=email/)call sites aftergotoSyncSession. This branch has nine, and four of them are undertests/cms/. That work belongs in its own pull request.