Skip to content

BottomSheetModal fires onDismiss when dismiss() is called on a sheet that was never presented #2703

Description

@olivier-bouillet

Summary

BottomSheetModal's unmount() calls the user-provided onDismiss callback unconditionally, even when the modal was never actually presented/mounted. Calling ref.current?.dismiss() on a modal that was never present()-ed therefore fires onDismiss.

Where

src/components/bottomSheetModal/BottomSheetModal.tsx, in unmount():

const hadReactMount = mountRef.current;
// ...
// unmount the node, if sheet is still mounted in React state
if (hadReactMount) {
  setState(INITIAL_STATE);
}

// fire `onDismiss` callback
if (_providedOnDismiss) {
  _providedOnDismiss();   // <-- fires even when hadReactMount === false
}

The code already computes hadReactMount and uses it to gate the React unmount (setState), but the onDismiss callback is fired without the same guard. This is internally inconsistent: onDismiss semantically means "a presented sheet was dismissed", yet it fires for a sheet that was never mounted.

Reproduction

  1. Render a BottomSheetModal permanently (kept mounted, never call present()).
  2. Call ref.current?.dismiss() (e.g. from an effect that hides the sheet when a condition is false).
  3. onDismiss fires even though the sheet was never shown.

This bites consumers that perform side effects in onDismiss (e.g. navigation.goBack()): a phantom dismiss on mount navigates the user away. We hit this on react-native@0.85 / new architecture, where dismiss() on a never-presented permanently-mounted modal triggers the unconditional onDismiss.

Suggested fix

Gate the callback on the already-computed hadReactMount, mirroring the setState guard:

if (hadReactMount && _providedOnDismiss) {
  _providedOnDismiss();
}

Happy to open a PR (have one ready).

Environment

  • @gorhom/bottom-sheet: 5.2.14 (same code on current master)
  • react-native: 0.85.x (new architecture)
  • react: 19

Metadata

Metadata

Assignees

No one assigned

    Labels

    invalidThis doesn't seem right

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions