Conversation
…clearing its content
JohnathanWhite
left a comment
There was a problem hiding this comment.
The mechanism described here is real — I traced it. Clearing isModalVisible in the background handler short-circuits the !isVisible && isModalVisible guard in the effect below, so the imperative dismiss() never runs and the native sheet stays presented while React thinks it's gone. Skipping that state clear for bottom-sheet is the right shape of fix.
The new spec is a true regression test: with the fix reverted, dismisses the sheet when backgrounded, not just its content fails with 0 dismiss calls instead of 1. Both tests pass with the fix in.
One gap worth a follow-up (not a blocker for this PR). After this change, native dismissal depends entirely on the parent flipping isVisible in response to onBackdropPress. BottomNotification deliberately makes that callback a no-op when enableBackdropDismiss is false:
const handleBackdropPress = useCallback(() => {
if (enableBackdropDismiss) {
dispatch(AppActions.dismissBottomNotificationModal());
...
}
}, [enableBackdropDismiss, dispatch, onBackdropDismiss]);So for those callers — Passkeys.tsx:230, Theme.tsx:54, ThorswapDetails.tsx:224 — backgrounding the app now changes nothing at all and the sheet stays presented. I wrote a throwaway test for that case and got 0 dismiss() calls both before and after this patch, so it's a pre-existing gap rather than something this PR breaks. Worth its own ticket: if the point of the background path is keeping sheet content out of the app-switcher snapshot, the component should dismiss imperatively on background instead of trusting the caller to flip the controlled prop.
Related, also pre-existing: when Gorhom dismisses on its own, handleDismiss doesn't sync isModalVisible back to false, so the two can disagree if a caller leaves isVisible true.
Verified and fine:
- Dependency array is complete —
isVisible,fullscreen,onBackdropPress,modalLibraryare all there, and the effect re-subscribes the AppState listener on change, so no stale closure. - The
react-native-modalbranch and fullscreen sheets are genuinely untouched. - Rapid close/reopen still works — the
isDismissingRef/pendingOpenRefqueueing is undisturbed. - Spec hygiene is fine:
restoreAllMockscovers both the prototype spies and theAppState.addEventListenerspy, and thererender/treeself-reference is lazily evaluated so it's valid.
One note on the test's reach: it spies on the @gorhom/bottom-sheet stub from test/setup.js, not the real class. That's the right call for asserting SheetModal's imperative calls, but it validates nothing about native lifecycle or unmount-while-backgrounded.
LGTM with the follow-up filed separately.
RN-2975