Justin Priede
Justin Priede

Reputation: 119

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>;
  }
}


Upvotes: 6

Views: 2677

Answers (2)

gentlee
gentlee

Reputation: 3717

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.

Upvotes: 1

Jacky Wijaya
Jacky Wijaya

Reputation: 107

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} />
   }
}

Upvotes: 1

Related Questions