fix(mobile): branch picker no longer renders twice per update#22132

closed1
main
GitHub fork: perf/mobile-branch-picker-memo
opened11d agobyOoliver
View on GitHubTwo-way sync enabledPR last imported 9d ago

Note

🤖 Claude Opus 5.5 on behalf of Oliver

On mobile, the branch picker rendered twice for every update. After the React Compiler migration #21137, usePaginatedBranches built a new Atom.make pages atom on every render, and the compiler didn't cache it. useAtomValue then subscribed to a fresh atom each time, which forced a second render, and refresh changed identity every render.

The fix keys the pages atom through an Atom.family on the target and its cursors, so the same branch list always gets the same atom, with no useMemo. Initial cursors use null instead of undefined so the key survives JSON.stringify.

Proof:

  • In a jsdom harness running the compiled hook under a real AtomRegistry, 5 parent updates caused 10 renders on main and 5 here, and refresh changed identity 11 times on main and 0 here.
  • Ran vp test run apps/mobile/src/reactCompilerCoverage.test.ts, queries.test.ts and queries.filesystem.test.ts, plus mobile typecheck.
  • No regression test: the double render only appears in compiled output, and the mobile test runner doesn't apply the React Compiler, so a render test passes on main too. The compilerCachedCalls helper from #22913 doesn't fit either, because the fix stops creating the atom during render instead of caching it.
  • Not checked: a real device or simulator. There is no visible UI change, so no screenshots.

Claude Opus 5.5 via Claude Code in T3 Code

Checks

21 successful · 11 skipped
All checks passed
3d915e4
Synced from GitHub

Approvability

Verdict: Approved at 5fea58d

Macroscope's review found this PR approvable — This is an isolated mobile performance refactor that stabilizes paginated branch-query atoms and removes redundant rendering without changing branch data, pagination semantics, or product defaults. The limited runtime impact and contained scope make it low risk.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9d634c4d-9282-4349-a819-c41a76ff673e
📥 Commits

Reviewing files that changed from the base of the PR and between e0f853c66682ad192bb865a72b4302035bc9c31e and 5fea58de7d5d28e89fbbbf2e8f163b8495c529e9.

📒 Files selected for processing: 1
  • apps/mobile/src/state/queries.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

📝 Walkthrough

usePaginatedBranches now uses shared keyed atoms for branch-page fetching and aggregation. Initial cursors use null. Refresh targets the first shared branch-page atom.

Changes: Branch pagination

Layer / File(s)Summary
Shared branch page atoms
apps/mobile/src/state/queries.ts
The pagination state uses null for initial cursors. Shared atoms fetch and aggregate branch pages by serialized target key. The hook uses the keyed atom family and refreshes the first shared page atom when the environment and working directory exist.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 5fea5

The pagination change is ready to merge after normal checks; no branch-loading or refresh regression was identified.

Security architecture risk: 🔵 Low · up to 5fea5

The shared state remains separated by environment, repository directory, search query, and page cursors. No introduced security defect was identified, and the existing request authority is preserved. Upstream access enforcement was not fully verified.

Retained concerns No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated change affects branch-reference reads in the mobile new-task and scheduled-task pickers. Aggregate sharing is limited to identical environment, directory, query, and cursor keys; the inspected change adds no request authority or repository mutation. Upstream endpoint exposure and authorization remain unverified.

Trust Boundaries and Controls

  • observed — Page requests continue through the existing environment-scoped runtime to vcsListRefs. The aggregate key preserves the same environment and request-selection fields used by the underlying page family. This supports unchanged client-side isolation, but does not establish that upstream access enforcement is sufficient.
Pre-merge checks: Passed 4
Check nameStatusExplanation
Linked Issues checkPassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes checkPassedCheck skipped because no linked issues were found for this pull request.
Title checkPassedThe title clearly identifies the branch picker rendering regression, which matches the pull request’s main change.
Description checkPassedThe description clearly explains the rendering problem, the implementation change, measured results, tests, limitations, and agent attribution. It does not explicitly provide a Scope and approval sect…
Full details: Description check

Explanation

The description clearly explains the rendering problem, the implementation change, measured results, tests, limitations, and agent attribution. It does not explicitly provide a Scope and approval section or state why the focused fix qualifies without prior approval, but the linked related PRs and detailed technical context make the description mostly complete.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Added 1 commit

  • fix(mobile): branch picker no longer renders twice per update3d915e4

Folded into #22134. The branch hook returning new loadNext, refresh and branch lists on every render is what kept the new-task context changing even with #22134's provider fix, so the two belong in one PR. The commit moved over unchanged.