react-native-screens
react-native-screens copied to clipboard
Multiple transitions between screens causes app to crash
Description
Repeated navigation between 2 screens using React Navigation's Native Stack with a getId() prop causes the app to "crash". I use the term crash loosely as the behaviour is different per platform but both result in the app becoming unusable.
iOS
react-native elements that use Pressibility become stuck in their "onPress" state, preventing them from receiving further onPress events. The issue does not occur with non-Pressibility elements such as <TouchableOpacity />.
You can observe this behaviour with the reproduction snack by repeatedly moving between the two screens using the Forwards/Backwards <Text /> items. After 3 transitions, the <Text /> items are no longer pressable and will be rendered with their onPress highlighting.
Android
After 3 transitions, the native stack header will disappear. After 4 transitions, the native stack will no longer render any visible context.
Expo Router issue: https://github.com/expo/expo/issues/33658 React Navigation issue: https://github.com/react-navigation/react-navigation/issues/12346
Steps to reproduce
- Open Snack
- Click "forwards" / "backwards" 3-5 times
Snack or a link to a repository
https://snack.expo.dev/@mwlawlor/react-navigation-native-stack-stuck
Screens version
3.3.0
React Native version
0.76.3
Platforms
Android, iOS
JavaScript runtime
None
Workflow
None
Architecture
None
Build type
None
Device
None
Device model
No response
Acknowledgements
Yes
Facing the same issue right now
My app with expo-router is affected by this problem.
I am also facing this issue on Android. On iOS everything works fine.
When I navigate back to the same screen with the same getId, I observe the described behavior.
However, in my actual app, where I have a lot of integrations, this issue results in a crash with the following error:
The specified child already has a parent. You must call removeView() on the child's parent first
The temporary fix working for me now on Android is conditionally using navigateDeprecated if Platform is Android:
navigation.navigateDeprecated(name, params);
| Package | Version |
|---|---|
| react | 18.2.0 |
| react-native | 0.74.6 |
| @react-navigation/bottom-tabs | 7.2.0 |
| @react-navigation/native | ^7.0.14 |
| @react-navigation/native-stack | 7.2.0 |
| react-native-screens | ^4.4.0 |
| react-native-reanimated | 3.16.6 |
| react-native-gesture-handler | 2.21.2 |
| react-native-pager-view | ^6.6.1 |
This is causing Pizzahuts app to freeze on IOS 18 devices and simulators.
Does anyone have a work around?
Does anyone have a work around?
Use push, and dismiss. Instead of navigate
removing the getId prop from reproducer fixes the issue. react-native-screens does not have any getId related logic. Are there any premises that this is react-native-screens issue? @marklawlor
Okay, I have high level grasp of the issue mechanism now.
Let's have a following stack of screens: A B and let's set getId prop for both screens with constant values (not depending on route params).
Now, in react-navigation@v7, navigate(A) instead of popping the B, will reshuffle screens on the stack, to achieve the state B A.
When getId is not present this will work fine, because "new navigate" will just push a brand new screen A, instead of navigating the the one already on the stack, so the resulting stack state would be A B A.
In previous version of react-navigation the navigate(A) call would just pop B and "navigate to the screen already on stack as the method name suggests", which would result in stack state: A.
Now, the issue arises because reshuffling the screens on stack is not really supported & it seems that it has not been the case for a longer while, as it was not needed. Moreover, the crash that happens (and e.g. causes the header to disappear) suggests that this is due to asynchronous way we do the updates - which won't be easily changed.
Immediate solution
use navigateDeprecated instead of navigate in your apps
Proper solution
One of:
- restore original behaviour of
navigatefrom beforereact-navigation@v7, - change the behaviour of
navigatefornative-stackonly, - rewrite container update logic of screens - dunno if that's feasible - we do it in asynchronous way for a reason.
Tagging @satya164 as I think we should handle this on react-navigation level. Current behaviour of navigate is against stack paradigm leading to above issue. Do you think restoring navigate behaviour in future react-navigation versions is feasible?
@kkafar this behavior now being default with navigate only makes it easier to see the problem, but it's not the only way to surface the issue (e.g. you can also have this issue with reset or other actions - in v6 it can happen when using push and getId) so changing it will only make the issue less common - but not fix it. it's fundamental to how react navigation works and declarative nature of react.
from native perspective, is it not possible to take the same view containing the screens and render it in a new screen if it was reordered?
I haven't debugged it to very bottom, because the interaction between react-native's native mounting-stage code & Android Fragment transactions we use is quite complex, but the issue arises due to following:
- Having stack view
Swith two childrenA B - UI block 1: React removes B from S,
- UI block 1: we schedule removal of B from S on the UI queue (block 2),
- UI block 1: React inserts B at index 0 into S,
- UI block 1: we schedule removal of A, insertion of B and insertion of A on the UI queue (block 3)
- block 2 executes
- block 3 executes
The issue appears at point 4 - react tries to insert B, however it has been not yet effectively detached and still has a parent (still attached to S), because we didn't perform the update - we've just scheduled it for later execution.
Idk what would be technical solution here yet. I'll start discussion internally on how we could start supporting such "reshuffling".
I still wonder whether allowing for screen reshuffling in stack-based navigators is ok. From my current perspective this seems against the 'stack' logic. I wonder what are the use cases that people might consciously choose reshuffling the screens in stack. My guess is that most of the time this decision is not deliberate.
What package is navigateDeprecated in? I can't see it under CommonActions?
@pizzascott its on navigation object in react-navigation https://reactnavigation.org/docs/navigation-object/#navigatedeprecated
Ah I think we are on an older version so already using deprecated. Which is why android is fine for our app, but IOS is breaking.
Still haven’t found a work around and seems to happen when changing tab index also.
Temp fix for IOS is to use StackActions.replace
Just an update on our situation.
We are on "react-native-screens": "~3.29.0"
We experienced a bug that switching to a certain tab would freeze our app on IOS only.
We fixed it by switching to .replace instead of .navigate.
The same problem happens to me using expo-router , it’s a game stopper. Using tab with a stack for each tab . When i navigate back and forth within the stack / to different stacks , the stack becomes blank . It gets dismissed somehow. One time touchable opacity within the stack became unclickable . I tried a mix of router.push , router.replace , router.navigate . I ended up using push but it didn’t fix all the problem . Tho it happens less now . I am on Android .
I just wanna update that I'm looking into the issue. However current approach is to forbid certain kinds of navigation patters as they do not make much sense on stack-based navigator. I'm building the case 😅
I am quite concerned that this issue is not getting more priority. :worried:
Using Expo (or RN), it is really a problem if solid navigation cannot be guaranteed. Navigating between screens is such a core feature to any app.
We will release our Expo app pretty soon to production and I am not sure how to explain to stakeholders that something basic such as navigating is able to crash. :sweat_smile:
Reading the previous comments, it seems there is not really a solid workaround yet? Or did someone had success with replacing all <Pressable/ > with <TouchableOpacity /> or only using router.push()?
Thanks in advance for any help on this!
I don't know if the cause is the same, but I'm having this issue and I'm not even using getId, just navigation.push() onto a screen with presentation: "modal". If I repeatedly open and close the screen quickly on iOS (18.4), the app eventually crashes with the Modally presented controllers are being reshuffled, this is not allowed error.
Interestingly, if I replace navigation.push() with navigation.navigate(), the issue seems to go away.
| Package | Version |
|---|---|
react-native |
0.78.2 |
@react-navigation/native |
7.1.5 |
@react-navigation/native-stack |
7.3.9 |
react-native-screens |
4.10.0 |
Running into this issue as well. Is this going to be addressed?
Running into this issue using Expo Router also.
This issue should be resolved in SDK 53 for Expo Router. If you are still experiencing this issue after upgrading, please create a new issue on the expo repo as it is most likely not related to this issue.
@marklawlor it has not been resolved in SDK 53, it's still there. Just tested it (expo 53.0.9). There is an open issue assigned to you https://github.com/expo/expo/issues/33658#issuecomment-2878154551 (from December 13 2024) which is essentially the same bug. I wonder when it gets prioritized as this is a blocker for anyone who want to use New Architecture.
Anybody have solid workaround for this in Expo SDK 52?
jumping to start RN for a day and already found this kind of bug LMAO 😺
It still happens in expo SDK 53, Android only, app crashes with
The specified child already has a parent. You must call removeView() on the child's parent first
We have Tab bar, each has a stack inside. On the main tab we have a banner at the top of the page that takes user to another tab router.navigate('/anotherTab');. This works fine if stack in anotherTab is at the root, but if user was in anotherTab before, navigated to a nested screen then used tab bar to go back to main tab and used that banner that calls router.navigate('/anotherTab'); it will crash. iOS works fine and takes user back to root of another tab. I know it is somewhat an anti-pattern and bad UX to switch tabs like that, but it should still work.
This issue has been open for a while now. A significant amount of my users experience these crashes on Android.
I appreciate there are other issues and features to work on, but this is one of the most upvoted issues in the repo showing the probability of this happening and severity of this issue to be relatively high.
Is there any chance of getting this looked into in the short term?
I managed to solve this issue in our Expo app by preventing unnecessary navigation stack buildup. It seems that Expo (or React Native) doesn't always handle a growing navigation stack gracefully. 🤔
At first, I was using Router.navigate() for everything (including navigation between the bottom tabs) under the assumption that this was fine, since users could then press back to return to previous screens. But this ends up continuously adding new entries to the navigation history, which causes the stack to grow indefinitely (and Expo or React Native applying potential erroneous internal stack management to it?).
Eventually, I realised that users don't expect to press "back" after switching tabs. So I switched to using Router.replace() when handling tab navigation. That way, the navigation stack stays much cleaner and doesn't bloat over time. This fixed it for us. 👍🏼
This is why I absolutely loath react native, especially on android. Write once, debug everywhere. I have a simple list of profiles and a dynamic route [id], every third router.push on the list results in a blank screen. Pressing the tab button to return back briefly shows the missing screen for a split second before going back. Next 2 navigations works fine. This is such an absolutely dumb easy use case I cant even imagine the kind of rubbish that'll happen as the app gets more complicated.
I used to use react navigation 2 years ago and for all its complexity, I've never had these kinds of silly issues.
Edit: This is possibly the biggest piece of turd bug I've seen yet, and mind you I've seen so so many over the years. Somehow adding a StatusBar from expo status bar made the screen appear. Finding this thing was a complete fluke seriously what the actual $$$ guys :/
Hi everyone, I’m also running into this bug on both iOS and Android (Occurs much more frequently on iOS).
Exactly as @eeshankeni described: after several navigations, the page content randomly turns blank (layout content is still visible), then briefly flashes before switching to the next page. In my case it only happens with the tabBar (expo-router) using navigation.navigate.
Any updates or new workarounds before an official fix?
New edit : I found a workaround that solved the problem: I removed the transition animation between pages when navigating via the tab bar.