Skip to content

fix(ios): retry blocked auto-present after recycled controller sessions - #800

Open
maxlapides wants to merge 3 commits into
lodev09:mainfrom
maxlapides:mlapides/v4-present-lifecycle-fixes
Open

fix(ios): retry blocked auto-present after recycled controller sessions#800
maxlapides wants to merge 3 commits into
lodev09:mainfrom
maxlapides:mlapides/v4-present-lifecycle-fixes

Conversation

@maxlapides

Copy link
Copy Markdown

Summary

This forward-ports the iOS lifecycle fixes from #793 onto main (the v4 line), as offered in that PR's discussion.

A fast dismiss/re-present cycle can recycle the Fabric host while its retained controller is still finishing the previous presentation session. This fixes four concrete failure families:

  • Blocked auto-present: a replacement mount can find the controller dismissing, skip initialDetentIndex auto-presentation, and remain permanently dead because nothing retries it. Initial presentation now retries after attachment, prop updates, or dismissal completion, with deferred work scoped to the current mount and captured presentation intent.
  • Previous-session event leakage: presentation, dismissal, focus, drag, detent, and position callbacks from the previous mount can otherwise reach the replacement mount. Controller-session ownership now scopes those events and teardown to the mount that started the session.
  • Recycled-host state carryover: the replacement can inherit the previous mount's container, pending props/detents/layout work, navigation state, or dismissal snapshot. Recycle now detaches the old container, resets per-mount transient state, preserves the snapshot only through the dismissal that needs it, and promotes the replacement's pending detents before its retry presentation.
  • Registered-tag leak: React Native zeroes view.tag before prepareForRecycle, so unregistering self.tag removed key 0 while TrueSheetModule's strong registry retained every recycled host. The view now stores the tag it registered and unregisters that exact value.

The v4 port also adds review-driven hardening for v4-specific asynchronous paths: the interactive-navigation dismissal completion is mount-scoped; pending detents are applied before retry presentation and active-index clamping; and delayed detent/position emissions are presentation-session-scoped, including the 0.1-second transition-end settle.

This is the v4 counterpart to #793 and fixes the TrueSheet auto-present/lifecycle half of #789. It does not claim to fix the remaining #789 symptom where a presented sheet's second-commit subtree is not inserted until a later unrelated commit; as documented in #793, that is an upstream React Native Fabric transaction issue.

The CHANGELOG.md entry will be pushed immediately after this PR number exists.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Test Plan

  • The underlying lifecycle fixes were device red/green verified on the v3 line in fix(ios): Retry blocked auto-present after recycled controller sessions #793 using a physical iPhone 11 running iOS 26 with React Native 0.86.2 Fabric:
    • Unpatched: 158/316 mid-dismissal remounts wedged; 134/134 mid-dismissal content swaps wedged.
    • Patched: 0 wedges across 325 remount cycles and 294 content-swap cycles (619 total).
  • This v4 forward-port was verified by static lifecycle analysis and two independent, multi-round code reviews covering host recycle ordering, controller-session ownership, registry lifetime, and delayed callbacks.
  • yarn typecheck passes.
  • yarn lint passes.
  • yarn test --watchman=false passes (65 tests).
  • No device soak was run against the v4 branch.

Screenshots / Videos

Not applicable; this changes lifecycle handling without changing the sheet's visual design.

Checklist

  • I tested on iOS
  • I tested on Android
  • I tested on Web
  • I updated the documentation (not needed)
  • I added a changelog entry (will be pushed immediately after this PR number exists)

maxlapides and others added 2 commits August 24, 2026 13:34
…ions

Fast dismiss and re-present can recycle a view while its controller is still dismissing, leaving auto-present blocked with no later retry. Scope deferred presentation and queued sizing to the current mount, suppress stale controller-session events, and retry after dismissal completes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…odule

React Native zeroes view.tag before prepareForRecycle, so unregistering self.tag removes key 0 while TrueSheetModule retains the recycled host. Store the registered tag and unregister that value during recycle and deallocation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

@maxlapides is attempting to deploy a commit to the lodev09 Team on Vercel.

A member of the Team first needs to authorize it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant