expo: Unable to route between Stacks inside Tabs

Minimal reproducible example

https://github.com/VersionOneDev/expo-routing-issue

Which package manager are you using? (Yarn is recommended)

npm

If the issue is web-related, please select the bundler (web.bundler in the app.json)

metro

Summary

I have an layout which is made up of a Stack, three Tabs within the Stack, and then a Stack in each Tab.

When I route via router.push from one Tab to another, I am unable to navigate back up the Stack in the Tab which I have pushed to unless I have already manually navigated to the Tab which I have pushed to.

✅ This Works - Tab One -> Tab Two -> Tab Three -> Tab Two Detail -> Back Button:

https://github.com/expo/expo/assets/2962587/cbc19459-5b0b-463d-90cc-8489f6ca09e1

✅ This Works - Tab One -> Tab Two -> Tab Three -> Tab Two Detail -> router.back():

https://github.com/expo/expo/assets/2962587/653ee7b6-6ff1-4837-8e3e-2c8f6fcfa601

✅ This Works - Tab One -> Tab Two -> Tab Three -> Tab Two Detail -> Swipe Back:

https://github.com/expo/expo/assets/2962587/8b74e8a0-11ac-4c92-b551-856f40f6ac9e

❌ This Doesn’t Work - Tab One -> Tab Three -> Tab Two Detail -> Back Button:

I’d expect there to be a Back Button to take me to Tab Two, there is no Back Button.

https://github.com/expo/expo/assets/2962587/5e22497f-3575-4bc6-9f0f-e01eecd24e27

❌ This Doesn’t Work - Tab One -> Tab Three -> Tab Two Detail -> router.back():

I’d expect router.back() to take me to Tab Two, not Tab One.

https://github.com/expo/expo/assets/2962587/66523985-e7fc-43f6-90c4-f96d2df9441a

❌ This Doesn’t Work - Tab One -> Tab Three -> Tab Two Detail -> Swipe Back:

I’d expect Swipe Back to take me to Tab Two, there is no ability to Swipe Back.

https://github.com/expo/expo/assets/2962587/05e276bb-e8d2-4819-8496-ca93025a9727

❌ This Doesn’t Work - $ npx uri-scheme open exp://127.0.0.1:8081/--/home/tabTwo/tabTwoDetail --ios:

I’d expect there to be a Back Button to take me to Tab Two, router.back() to take me to Tab Two, not Tab One, and Swipe Back to take me to Tab Two.

https://github.com/expo/expo/assets/2962587/48fc0cbf-de1b-479f-b6c9-fcbfe881e63e

Once this has happened, there is no way to get back to Tab Two at all, as while router.back() takes me to Tab One, navigating to Tab Two via the Tabs takes me to Tab Two Detail, rather than Tab Two.

https://github.com/expo/expo/assets/2962587/8ac2f4b5-2631-48da-aa59-fa59be7544fb

I have attempted to add:

export const unstable_settings = {
  initialRouteName: "index",
};

To app/_layout.js, as well as app/home/tabOne/_layout.js, app/home/tabTwo/_layout.js, and app/home/tabThree/_layout.js, however this does not change the behaviour.

Environment

$ npx expo-env-info

  expo-env-info 1.0.5 environment info:
    System:
      OS: macOS 12.7.1
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 16.19.1 - ~/.nvm/versions/node/v16.19.1/bin/node
      npm: 8.19.3 - ~/.nvm/versions/node/v16.19.1/bin/npm
    SDKs:
      iOS SDK:
        Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1
    IDEs:
      Xcode: 14.2/14C18 - /usr/bin/xcodebuild
    npmPackages:
      expo: ~49.0.15 => 49.0.21 
      react: 18.2.0 => 18.2.0 
      react-native: 0.72.6 => 0.72.6 
    npmGlobalPackages:
      eas-cli: 5.2.0
    Expo Workflow: managed

About this issue

  • Original URL
  • State: open
  • Created 6 months ago
  • Reactions: 8
  • Comments: 17 (1 by maintainers)

Most upvoted comments

Any updates on this issue? Huge blocker for our march release. Shall I switch to React Native Navigation until then?

I was able to get the behavior hackily working using the navigation object instead of the router object, and using the “initial: false” option.

Something like:

const navigation = useNavigation()

<Pressable
    onPress={() => {
        navigation.navigate(<stackName>, {
          screen: <screenNameInStack>,
          params: { <queryParams> },
          initial: false // This renders the initial screen in the stack even when navigating to the nested screen?
        })
     }}
 >

I also used a custom back button to have more control over the routing

https://reactnavigation.org/docs/nesting-navigators/

this issue deservers much more attention. I’m glad to found @kaustubnvd workaround. many thanks!

I was able to get the behavior hackily working using the navigation object instead of the router object, and using the “initial: false” option.

Something like:

const navigation = useNavigation()

<Pressable
    onPress={() => {
        navigation.navigate(<stackName>, {
          screen: <screenNameInStack>,
          params: { <queryParams> },
          initial: false // This renders the initial screen in the stack even when navigating to the nested screen?
        })
     }}
 >

I also used a custom back button to have more control over the routing

https://reactnavigation.org/docs/nesting-navigators/

Hey all, it may be easier to create a patch file. That way you don’t need to change all the calls from router.navigate to navigation.navigate:

diff --git a/node_modules/expo-router/build/global-state/routing.js b/build/global-state/routing.js
index dfd96d110aaa01e014ed4172d7333092f708243d..6a40662a213c1b6e9051199854e0a06412c36332 100644
--- a/node_modules/expo-router/build/global-state/routing.js
+++ b/node_modules/expo-router/build/global-state/routing.js
@@ -150,6 +150,9 @@ function getNavigateAction(state, parentState, type = 'NAVIGATE') {
     else if (type === 'REPLACE' && parentState.type === 'tab') {
         type = 'JUMP_TO';
     }
+
+    params.initial = false
+
     return {
         type,
         target: parentState.key,

@kpoelhekke Version 3.5.x is current in beta and we’re finalising it for SDK 51. I haven’t verified that this particular issue has been resolved yet, but it includes many fixes for navigating across nested layouts.

From your description, it seems like your experiencing a number of different issues that may or may not relate the discussion at hand. Its hard to provide an answer without knowing the specifics. I would recommend creating an issue for each one so I can provide correct information.

@marklawlor what can be expected from expo-router regarding this issue? Is this really a bug or is this expected behavior?

We have a pretty large app, and this behavior easily lead to bugs when navigating between tabs via deeper nested stacks. It will become very difficult to keep track of all components that have inter-tab navigation. In all those locations we have to make sure the initial route is rendered before the actual navigation event is dispatched.

Also a lot of times the <Redirect /> component sends users to a screen in a different tab and they’re not able to go back. We have some workarounds in place now, but it is definitely a big concern here and causes cognitive overhead for our developers going forward.

The params.initial = false also doesn’t always work when working with nested Stacks.

Would be nice to know if this is on the roadmap or if this is too big of a breaking change to implement.

the patch worked for me

Hey all, it may be easier to create a patch file. That way you don’t need to change all the calls from router.navigate to navigation.navigate:

diff --git a/node_modules/expo-router/build/global-state/routing.js b/build/global-state/routing.js
index dfd96d110aaa01e014ed4172d7333092f708243d..6a40662a213c1b6e9051199854e0a06412c36332 100644
--- a/node_modules/expo-router/build/global-state/routing.js
+++ b/node_modules/expo-router/build/global-state/routing.js
@@ -150,6 +150,9 @@ function getNavigateAction(state, parentState, type = 'NAVIGATE') {
     else if (type === 'REPLACE' && parentState.type === 'tab') {
         type = 'JUMP_TO';
     }
+
+    params.initial = false
+
     return {
         type,
         target: parentState.key,

Thank you so much, it saved me a lot of time of debugging…

In case someone will try to adapt it on expo-50:

diff --git a/node_modules/expo-router/build/global-state/routing.js b/node_modules/expo-router/build/global-state/routing.js
index 2fba9d1..9cce0c9 100644
--- a/node_modules/expo-router/build/global-state/routing.js
+++ b/node_modules/expo-router/build/global-state/routing.js
@@ -173,6 +173,9 @@ function getNavigateAction(state, parentState, type = 'NAVIGATE') {
     else if (type === 'REPLACE' && parentState.type === 'tab') {
         type = 'JUMP_TO';
     }
+
+    params.initial = false
+    
     return {
         type,
         target: parentState.key,

Just issue two commands. First one command that switches tab and then the one that pushes to stack. You might have to put a small delay between the. Like an await on a promise that resolved after a few ms

On Thu, 11 Jan 2024 at 21:32, Chrıs Seelus @.***> wrote:

@killerchip https://github.com/killerchip How did you trigger two navigation actions?

Similar problem here. My app consists of five tabs, each with its own stack and linking from the first tab directly to a page nested inside another tab, won’t show a back button, resulting in the tab being trapped on the nested page.

— Reply to this email directly, view it on GitHub https://github.com/expo/expo/issues/26211#issuecomment-1887831484, or unsubscribe https://github.com/notifications/unsubscribe-auth/AELMPHWKU6QHXCFVQHKC2UTYOA433AVCNFSM6AAAAABBKK53K2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQOBXHAZTCNBYGQ . You are receiving this because you were mentioned.Message ID: @.***>