Skip to content

fix(settings): start the /pair Sync bootstrap once per mount - #21237

Open
vbudhram wants to merge 1 commit into
mainfrom
fxa-14544
Open

vbudhram wants to merge 1 commit into
mainfrom
fxa-14544

Conversation

@vbudhram

Copy link
Copy Markdown
Contributor

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

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).
  • I have manually reviewed all AI generated code.

How to review (Optional)

  • Key files/areas to focus on: the dependency array of the bootstrap effect in Pair/Index/index.tsx, and the two refs above it.
  • Suggested review order: read index.tsx first, then the three new tests in index.test.tsx.
  • Risky or complex parts: the lifetime of 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:

  • Full fxa-settings Jest suite: 302 suites, 3768 tests, 0 failures.
  • npx tsc -p packages/fxa-settings/tsconfig.json --noEmit: clean.
  • npx nx lint fxa-settings: exit 0.
  • Two of the three new tests fail against unmodified HEAD, so they pin the regression. I did not run the Playwright functional tests.

useFxAStatus stays unmemoized on purpose. The acceptance criteria accept either approach. A memoized return object needs offeredSyncEngines memoized 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 after gotoSyncSession. This branch has nine, and four of them are under tests/cms/. That work belongs in its own pull request.

## 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
@vbudhram
vbudhram requested a review from a team as a code owner September 16, 2026 17:52
@vbudhram vbudhram added the auto label Sep 16, 2026
Copilot AI balanced review requested due to automatic review settings September 16, 2026 17:52
@vbudhram vbudhram added the auto label Sep 16, 2026
@vbudhram vbudhram self-assigned this Sep 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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.

@dschom

dschom commented Sep 17, 2026

Copy link
Copy Markdown
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 dschom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+wc

@dschom
dschom self-requested a review September 18, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants