Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

navigating from a tab screen to a stack screen

For an IOS application, I have a stack that gets called in my tab navigator. I am trying to navigate from a bottom tab screen to a screen in the stack but I am getting the following error.

undefined is not an object (evaluating '_this.props.navigation.navigate')

I would like to render the bottom tab across all screens. I am noticing this causes some interesting issues with goBack() as well in other places.

How can I navigate from the bottom tab screen to a stack screen?

Is the current implementation a bad practice?

I have provided this demo as well as the following code below. I think it is related to prop passing.

const Tab = createBottomTabNavigator();

function MyTabs() {
  return (

     <Stack.Navigator
     initialRouteName="Home">        
      <Stack.Screen name="Home" component= {Home} options={{headerShown: false}}/>
      <Stack.Screen name="Screen1" component= {Screen1} options={{headerShown: false}}/> 
    </Stack.Navigator>
);

);
}

export default function App() {

  return (
  <NavigationContainer>
    <Tab.Navigator
    initialRouteName="Home"
    screenOptions={{
      tabBarActiveTintColor: '#F60081',
      tabBarInactiveTintColor: '#4d4d4d',
      tabBarStyle: {
        backgroundColor: '#d1cfcf',
        borderTopColor: 'transparent',
      },
    }} 
  >
    <Tab.Screen
      name="Home"
      component={MyTabs}
      options={{
        tabBarLabel: 'Home',
        headerShown: false,
        tabBarIcon: ({ color, size }) => (
          <MaterialCommunityIcons name="home" color={color} size={size} />
        ),
      }}
    />
    
  </Tab.Navigator>
</NavigationContainer>
);
}

const Stack = createStackNavigator();

import * as React from 'react';
import {
  Text,
  View,
  StyleSheet,
  TouchableOpacity,
  ImageBackground,
} from 'react-native';
const Images = [
  { id: '1', uri: require('./assets/snack-icon.png'), text: 'Test' },
  { id: '2', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
  { id: '3', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
  { id: '4', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
];

export default class Home extends React.Component {

thisNavigate = () => {
  this.props.navigation();
};

  renderList = (props) => {
    return Images.map((item, i) => {
      return (
          <TouchableOpacity
            onPress={() => this.props.thisNavigate.navigate('Screen2')}>
            <ImageBackground
              source={item.uri}
              style={{
                width: '100%',
                height: 100,
                shadowColor: '#000',
                shadowOffset: { width: 1, height: 4 },
                shadowOpacity: 1,
              }}
              imageStyle={{ borderRadius: 10 }}></ImageBackground>
          </TouchableOpacity>
      );
    });
  };

  render() {
    return <View style={{ flex: 1 }}>{this.renderList()}</View>;
  }
}


like image 369
Justin Priede Avatar asked Dec 18 '21 01:12

Justin Priede


People also ask

How do I combine stack Navigator and Tab Navigator?

Firstly, we will set up the tab navigator and then go further to add the stack navigator inside. Create a new folder with the name Screens within our project folder. Now inside the Screens folder create three files i.e. Screen1. js, Screen2.

What is the difference between stack Navigator and Tab Navigator?

StackNavigator: When user taps a link, a new screen is put on top of old screens. TabNavigator: User navigates to different screens by tapping on tabs along the top or bottom of the screen.


2 Answers

In your example your Home component is:

export default class Home extends React.Component {
  render() {
    return <Screen1/> // here navigation is not passed because it is rendered not by navigator but manually
  }
}

Just remove it, remove its Stack.Screen from MyTabs and set initialRouteName to Screen1, and it will work.

EDIT:

If you want to keep Home as it is, pass navigation like that:

export default class Home extends React.Component {
  render() {
    return <Screen1 navigation={this.props.navigation}/>
  }
}

Or use useNavigation hook instead of props in Screen1 to get navigation.

EDIT2:

Working demo.

like image 131
Alexander Danilov Avatar answered Oct 17 '22 14:10

Alexander Danilov


I think that you need to wrap your component withNavigation HOC https://reactnavigation.org/docs/4.x/with-navigation/

That's because your component not directly from the component Navigator, so they don't have this.props.navigation, to make your component have navigation props in Class Component, you need to wrap your component using withNavigation HOC

example:


// Screen1.js
class Home extends React.Component {
  renderList = (props) => {
    return Images.map((item, i) => {
      return (
          <TouchableOpacity
            onPress={() => this.props.navigation.navigate('Screen2')}>
            <ImageBackground
              source={item.uri}
              style={{
                width: '100%',
                height: 100,
                shadowColor: '#000',
                shadowOffset: { width: 1, height: 4 },
                shadowOpacity: 1,
              }}
              imageStyle={{ borderRadius: 10 }}></ImageBackground>
          </TouchableOpacity>
      );
    });
  };

  render() {
    return <View style={{ flex: 1 }}>{this.renderList()}</View>;
  }
}

export default withNavigation(Home);

sorry, but I think withNavigation is removed from v5 / v6

but you can make one like this

import { useNavigation } from '@react-navigation/native';

// withNavigation.js
export default function withNavigation(WrappedComponent) {
   return (props) => {
      const navigation = useNavigation();
      return <Component navigation={navigation} {...props} />
   }
}
like image 1
Jacky Wijaya Avatar answered Oct 17 '22 15:10

Jacky Wijaya