Skip to content

fix(adapters): recover gorhom from dropped onChange; drop dead swmansion settle guard#30

Merged
arekkubaczkowski merged 1 commit into
mainfrom
fix/adapter-open-close-races
Jun 4, 2026
Merged

fix(adapters): recover gorhom from dropped onChange; drop dead swmansion settle guard#30
arekkubaczkowski merged 1 commit into
mainfrom
fix/adapter-open-close-races

Conversation

@arekkubaczkowski

Copy link
Copy Markdown
Owner

Two open/close lifecycle race fixes in the adapters

gorhom — sheet stuck in 'opening', all opens blocked

Repro: open a sheet, tap switch, then immediately dismiss (backdrop/gesture) before it finishes opening → afterwards push / switch / replace do nothing (app responsive, buttons dead).

Cause: switch sets the sheet below to 'hidden' and starts closing it. Dismissing the new top re-reveals the below sheet ('hidden' → 'opening') and calls expand() while its close animation is still in flight. gorhom swallows that expand() and never emits onChange — the only signal that drove handleOpened. The sheet stays 'opening' forever, which permanently trips the hasOpeningInGroup guard in open(), so every subsequent open is a no-op.

Fix: report opened from the animated index (a reliable signal gorhom keeps updating even when it drops the JS callback) via a useAnimatedReaction — fire handleOpened when the index crosses into an open snap point. Idempotent (the coordinator's markOpen only acts while 'opening'). The existing onChange → handleOpened path is untouched; this is an additive backstop. Uses scheduleOnRN (not the deprecated runOnJS).

swmansion — drop the now-dead settle guard

onSettle is now optimistic (fires when the animation starts), so a settle at the collapsed detent only ever follows a real close — the old closeRequestedRef / hasOpenedRef guard against a spurious pre-open settle(0) no longer fires. Verified empirically (including the defaultIndex < 0 / persistent path): open emits settle(openIndex), close emits settle(0), and a mount-time settle(0) (if any) is absorbed by the coordinator's status !== 'hidden' check. The guard is removed; the settle is handled directly.

example

ScannerSheet (a persistent sheet) now uses the swmansion adapter, adding coverage for the swmansion + persistent (defaultIndex < 0) combination that previously had none.

No changes to the shared store / coordinator.

…ion guard

gorhom: under rapid open/close (switch then an immediate dismiss), gorhom can
drop its onChange, leaving a re-revealed sheet stuck in 'opening'. That
permanently trips the open() guard (hasOpeningInGroup), so every later open is
a no-op. Report opened from the animated index as a reliable backstop
(idempotent via the status guard).

swmansion: onSettle is now optimistic (fires when the animation starts), so a
settle at the collapsed detent only ever follows a real close. The
closeRequestedRef guard is dead — drop it and handle the settle directly.

example: ScannerSheet (persistent) now uses the swmansion adapter, covering the
swmansion + persistent (defaultIndex < 0) path.
@arekkubaczkowski arekkubaczkowski merged commit 57f955f into main Jun 4, 2026
3 checks passed
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