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