1

Having a bit of trouble getting to navigate between screens in the nested navigation (Tabs and screens). Here is my code:

This is the Tab Stack navigator

const AppStack = () => {
  return (
    <Tab.Navigator
    initialRouteName="Scan"
    screenOptions={{
      headerShown: false,
      tabBarActiveTintColor: 'red',
      tabBarInactiveTintColor: 'black',
    }}>
      <Tab.Screen name="Market" component={MarketplaceScreen} options={{
          tabBarLabel: 'Marketplace',
          tabBarIcon: ({ focused, color, size }) => (
            <Icon
                name="shopping"
                size={24}
                color={focused ? color : 'black'}
 
              />
          ),
        }} />
      <Tab.Screen name="Discover" component={DiscoverScreen} options={{
          tabBarLabel: 'Discover',
          tabBarIcon: ({ focused, color, size }) => (
            <Icon
                name="map-search"
                size={24}
                color={focused ? color : 'black'}
 
              />
          ),
        }} />
      
        <Tab.Screen name="Wallet" component={WalletScreen} options={{
          tabBarLabel: 'Wallet',
          tabBarIcon: ({ focused, color, size }) => (
            <Icon
                name="wallet"
                size={24}
                color={focused ? color : 'black'}
 
              />
          ),
        }} />
        <Tab.Screen name="Account" component={AccountScreen} options={{
          tabBarLabel: 'Account',
          tabBarIcon: ({ focused, color, size }) => (
            <Icon
                name="account"
                size={24}
                color={focused ? color : 'black'}
 
              />
          ),
        }} />
        
    </Tab.Navigator>
  );

This is the Screen Stack:

  function App() {
    return (
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen
            name="Profile"
            component={ProfileScreen}/>
        </Stack.Navigator>
      </NavigationContainer>
    );
  }

This is how I set up the Account Screen:

  function AccountScreen(navigation:any) {
      return (
        <View style={styles.container}>
              <TouchableOpacity onPress={() =>navigation.navigate('Profile') }><Text>PRESS HERE </Text></TouchableOpacity>
    </View>

The error I keep getting is "navigation.navigate is not a function"

Any help is welcome, I'm just out of ideas at the moment, Thank you

mick1996
  • 516
  • 9
  • 31

2 Answers2

1

It depends on the following question: Do you want the ProfileStack to be a tab as well? If the answer is positive, then you need to nest the ProfileStack inside a tab. If the answer is negative, then you need to nest the AppStack inside the stack of the profile defined inside App.

Assuming that ProfileStack should not be a tab, then the solution is as follows.

function App() {
    return (
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen
            name="Profile"
            component={ProfileScreen}
          />
          <Stack.Screen
            name="Tabs"
            component={AppStack}
          />
        </Stack.Navigator>
      </NavigationContainer>
    );
  }

Notice, that ProfileScreen won't have a tab bar and that the initial screen is Profile. The only way for the user to navigate to a screen that contains a tab bar is some sort of button in ProfileScreen. This is usually not what you want.

If you want the ProfileStack to be a tab, then you need to do it the other way around.

const ProfileStack = () {
    return (
        <Stack.Navigator>
          <Stack.Screen
            name="Profile"
            component={ProfileScreen}
           />
        </Stack.Navigator>
    )
}

const AppStack = () => {
  return (
    <Tab.Navigator
    initialRouteName="Scan"
    screenOptions={{
      headerShown: false,
      tabBarActiveTintColor: 'red',
      tabBarInactiveTintColor: 'black',
    }}>
      <Tab.Screen name="ProfileStack" component={ProfileStack} options={{
          tabBarLabel: 'Profile',
          tabBarIcon: ({ focused, color, size }) => (
            <Icon
                name="profile" // or whatever
                size={24}
                color={focused ? color : 'black'}
 
              />
          ),
        }} />

...

And in your App you need to use the AppStack instead.

function App() {
    return (
      <NavigationContainer>
        <AppStack />
      </NavigationContainer>
    );
  }

Furthermore, you need to destructure the navigation object from the screen props using curly braces.

If ProfileStack is now actually a tab, you need to navigate as follows.

function AccountScreen({navigation}) {
      return (
        <View style={styles.container}>
              <TouchableOpacity onPress={() => navigation.navigate('ProfileStack') }><Text>PRESS HERE </Text></TouchableOpacity>
    </View>)
David Scholz
  • 8,421
  • 12
  • 19
  • 34
  • 1
    Hi David! really solid answer, thank you for putting in the time! I want to achieve the first part I do NOT want the profile stack to be a tab. Unfortunately, I am still getting the same error with your solution. have you any other ideas? – mick1996 May 18 '22 at 16:04
  • 1
    Error Read: TypeError: navigation.navigate is not a function. (In 'navigation.navigate('Profile')', 'navigation.navigate' is undefined) – mick1996 May 18 '22 at 16:06
  • 1
    Mmh this should not happen. Have you fixed the destructuring in `AccountScreen` as well by using curly braces? – David Scholz May 18 '22 at 16:07
  • when I deconstruct Account Screen I get "Binding element 'navigation' implicitly has an 'any' " – mick1996 May 18 '22 at 16:08
  • 1
    This is typescript. `function AccountScreen({navigation}: {navigation: any}) { ... }` should fix the type error, but does the navigation work now? – David Scholz May 18 '22 at 16:10
  • Now I get.... The action 'NAVIGATE' with payload {"name":"Profile"} was not handled by any navigator. – mick1996 May 18 '22 at 16:11
  • 1
    It should work, [here is a minimal snack](https://snack.expo.dev/@pgrepds/navigation-tab-stack). In this snack, the ProfileScreen is the first screen inside the stack, the second screen is a tab navigator. If you press the button, it navigates to the tab navigator. There is a button as well which navigates back to the profile screen. Notice that the tab vanishes again. – David Scholz May 18 '22 at 16:20
  • 1
    Ill try that now and update you, thank you very much for help! – mick1996 May 18 '22 at 16:22
  • Hey David, I finally got it to work and have detailed my answer below, Thank you again for your help! – mick1996 May 19 '22 at 09:13
0

I managed to solve this by making a blank expo project with tabs in typescript to see if I could spot the difference. This is how I got it to work:

I set up the tabs and app stacks by copying the expo project in the app.tsx.

I then had to create a types.tsx file which detailed all of the routes. This file is also generated by expo so its a straight copy and paste (With your naming conventions of course)

I had to change the account screen to:

function AccountScreen({ navigation }: { navigation: any }) { ... }

Then the navigation worked like:

      <TouchableOpacity onPress={() =>navigation.navigate('Profile') }><Text>PRESS HERE </Text></TouchableOpacity>

I then had to add this to the profile screen:

export default function ProfileScreen({ navigation }: RootStackScreenProps<'Profile'>) { ... }

This is a silly problem, so I hope my answer can help someone!

mick1996
  • 516
  • 9
  • 31