I have a Dropdown component which is just a React Native Modal
positioned next to a toggle - the Modal
allows me to make the whole background a Pressable
so I can close the dropdown when any area outside it is pressed.
The items inside the dropdown menu each have an onPress
prop which performs a given function while also closing the dropdown itself. This works great, except when I want to use the onPress
event to open another react-native Modal
.
Here's a (simplified) example:
<>
// Custom component that renders a react-native Modal
<Dropdown
items={[
{ label: "Press to open a Modal", onPress: () => setIsModalOpen(true) }
]}
/>
// Another react-native Modal
<Modal visible={isModalOpen}>
...
</Modal>
</>
This works as expected on the web - the Dropdown's Modal
closes and the other Modal
opens at the same time. However, on iOS, the second Modal
never opens, and the app actually becomes completely unresponsive until I restart it from the Metro builder.
I've seen other questions on Stack Overflow that mention "opening a modal from inside another modal", but the existing questions all seem to concern nested modals. In my case, we aren't actually trying to nest modals - the second modal should open as the first one closes. The iOS app seems to just not render the second modal, even though I can verify through the console that the isModalOpen
boolean is getting set to true.
I'm beginning to think this is actually a bug with React Native itself, but figured I'd check here in case it's a known issue, maybe with event bubbling or something?
So at the end of the day, the issue is that React Native simply won't show two modals at the same time - this limitation applies even if you're trying to open a new modal while the previous one's closing animation is still finishing up.
It seems like some people handle this with a timeout, but that proved unreliable in my testing. A timeout also relies on a magic number, when the crux of the issue is that the modal has yet to unmount before opening a new one.
To solve this, I added a state variable called queuedPress
to my Dropdown menu's context provider that stores the onPress
function from the menu item that was just pressed. I also added an afterClose
callback that runs when the Dropdown menu's closing animation completes. When a Dropdown item is pressed, I store its onPress
function, then afterClose
handles the actual call. This ensures the onPress
is queued for as long as the animation takes to complete, so the modal opened from within that onPress
will be guaranteed to open after the dropdown is already closed.
Depending on your code your implementation might vary wildly, but in my case this is another situation where useContext
has saved the day. Any solution that limits the number of open modals to 1 should work.