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>;
}
}
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.
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.
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.
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} />
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With