Skip to content

[Bug]: dismiss() on a never-presented BottomSheetModal permanently prevents all future present() (5.2.11+) #2713

Description

@pvap

Version

v5

Reanimated Version

v3

Gesture Handler Version

v2

Platforms

iOS, Android

What happened?

Since 5.2.11 (modal status rewrite, #2632), calling dismiss() on a BottomSheetModal that has never been presented permanently wedges the modal:
every subsequent present() mounts state but renders nothing. No error, no warning. The same call was a safe no-op on <= 5.2.10.

Root cause (from diffing 5.2.10 vs 5.2.14 source):

  1. handleDismiss early-exits only for CLOSED, MINIMIZED, and DISMISSING at index -1 — MODAL_STATUS.INITIAL is NOT covered. So a dismiss on a never-presented modal falls through to:
    statusRef.current = MODAL_STATUS.DISMISSING + bottomSheetRef.current?.forceClose() — but the ref is null (never mounted), so forceClose is a silent no-op and no callback ever transitions the status again. The modal is stuck in DISMISSING.

  2. handlePortalRender returns early while status is DISMISSING, so every later present() sets mount: true but the portal content is never rendered.

handlePresent never resets the status (it only sets ANIMATING when the sheet was already mounted), so there is no recovery path.

Common real-world trigger: a visible-prop wrapper component whose effect runs its else-branch on first mount:

useEffect(() => {
if (visible) modalRef.current?.present();
else modalRef.current?.dismiss(); // runs once at mount,
visible=false
}, [visible]);

This pattern worked for years on <= 5.2.10.
Likely related: #2703 — same trigger (dismiss() on a never-presented modal), different symptom (spurious onDismiss vs. permanently blocked present()).
Both stem from handleDismiss not early-exiting on MODAL_STATUS.INITIAL.

Reproduction steps

  1. Render a BottomSheetModal ref={ref} snapPoints={[300]}...
  2. Call ref.current.dismiss() before ever presenting (e.g. from an effect)
  3. Call ref.current.present()
  4. Nothing appears — and no present() call ever works again for this modal

Expected behavior:

dismiss() on a never-presented modal should be a no-op (as in <= 5.2.10), and present() should always be able to present. Suggested fix: include INITIAL (and DISMISSED) in handleDismiss's early-exit list, and/or reset statusRef in handlePresent.

Reproduction sample

https://snack.expo.dev/@pvap/bottom-sheet---issue-reproduction-template

Relevant log output

No output — the failure is completely silent. No error, no warning.
present() executes normally but handlePortalRender skips rendering while the internal MODAL_STATUS is stuck in DISMISSING.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions