GrapeJam
GrapeJam

Reputation: 181

React Native: Passing navigation route as props into dynamically rendered component

I am building an app in which several of the screens have dynamically rendered cards that are mapped to an array of objects called ENTRIES. Each one of these cards can be pressed to navigate to a corresponding screen, however, I cannot seem to get the navigation to work.

I am passing is the screen value from ENTRIES as props from the Settings.js screen into the ClickableCard component, which then gets passed into the TouchableOpacity onClick as this.props.navigation.navigate(screen).

However I keep getting the following error TypeError: undefined is not an object (evaluating '_this3.props.navigation.navigate')

Here is an example of the code below:

App.js File

    import React from 'react;
    import {createMaterialBottomTabNavigator} from '@react-navigation/material-bottom-tabs';
    import {NavigationContainer} from '@react-navigation/native';
    import {createStackNavigator} from '@react-navigation/stack';
    import Home from './src/screens/Home';
    import SettingsScreen from './src/screens/SettingsScreen';
    import PrivacyScreen from './src/screens/PrivacyScreen';
    import NotificationsScreen from './src/screens/NotificationsScreen';
    import SoundsScreen from './src/screens/SoundsScreen';
    import ThemeScreen from './src/screens/ThemeScreen';

    const PrivacyStack = createStackNavigator();
    const SettingsStack = createStackNavigator();
    const AuthStack = createStackNavigator();
    const MainStack = createStackNavigator();
    const Tabs = createMaterialBottomTabNavigator();

    const TabNavigator = () => {
     return (
    <Tabs.Navigator
    initialRouteName="Home"
    <Tabs.Screen
    name="Home"
    component={HomeStack}
    />
    Tabs.Screen
    name="Settings"
    component={SettingsStack}
    children={this.SettingsStack}
    </Tabs.Navigator>
    )
    }

const AuthStack = () => (
  <AuthStack.Navigator>
    <AuthStack.Screen
      name="Auth"
      component={Auth}
    />
  </AuthStack.Navigator>
);


    const SettingsStackScreen = () => (
      <SettingsStack.Navigator>
        <SettingsStack.Screen
          name="Settings"
          component={Settings}
        />
        <SettingsStack.Screen
          name="Privacy"
          component={PrivacyStack}

        />
        <SettingsStack.Screen
          name="Theme"
          component={ThemeScreen}

        />
        <SettingsStack.Screen
          name="Notifications"
          component={NotificationsScreen}

        />
        <SettingsStack.Screen
          name="Sound"
          component={SoundsScreen}

        />
      </SettingsStack.Navigator>
    );

    const PrivacyStack = () => (
      <PrivacyStack.Navigator>
        <PrivacyStack.Screen
          name="Privacy"
          component={PrivacyScreen}
        />
        <PrivacyStack.Screen
          name="Notifications"
          component={NotificationsScreen}

        />
      </PrivacyStack.Navigator>
    );

const App = () => {
return (
    <NavigationContainer ref={navigationRef}>
      <MainStack.Navigator>
        <MainStack.Screen name="Tabs" component={TabNavigator} />
        <MainStack.Screen
          name="Auth"
          component={AuthStack}
          options={{gestureEnabled: false}}
        />
      </MainStack.Navigator>
    </NavigationContainer>
)
}

Settings.js File

    import React, {Component} from 'react';
    import {TouchableOpacity, ScrollView} from 'react-native;
import ClickableCard from './ClickableCard'

        export default class Settings extends Component {
        render(screen, index) {
        return (
                  <ScrollView>
                    {ENTRIES.map((entry, index) => (
    <ClickableCard screen={entry.screen} key={entry.index}/>

                    ))}
                  </ScrollView>
        )
        }

        export default Settings

ClickableCard.js Component

                import React, {Component} from 'react';
                import {TouchableOpacity, ScrollView} from 'react-native;

                    export default class ClickableCard extends Component {
          constructor(props) {
            super(props);
        }
                    render() {

const {
screen,
key
} = this.props
                    return (
                            <TouchableOpacity

                                key={key}
                                onPress={() => this.props.navigation.navigate(screen)}>
                              </TouchableOpacity>
                    )
                    }
    }

entries.js File

import React from 'react';

export const ENTRIES = [
{
name: "Theme",
screen: "ThemeScreen",
},
{
name: "Sounds",
screen: "SoundsScreen",
},
{
name: "Notifications",
screen: "NotificationsScreen",
},
] 

Upvotes: 0

Views: 220

Answers (1)

Guruparan Giritharan
Guruparan Giritharan

Reputation: 16334

You are trying to access navigation outside the navigation stack.

If you are using a functional component you can go with the useNavigation hook but as this is a class based component you will have to send the navigation as a prop or you can do the below as suggested in the documentation

import { useNavigation } from '@react-navigation/native';
class ClickableCard extends Component {
  constructor(props) {
    super(props);
  }
  render() {
    const { screen, key } = this.props;
    return (
      <TouchableOpacity
        key={key}
        onPress={() =>
          this.props.navigation.navigate(screen)
        }></TouchableOpacity>
    );
  }
}

const ClickableCardWithNavigation= (props) {
  const navigation = useNavigation();

  return <ClickableCard {...props} navigation={navigation} />;
}

export default connect(ClickableCardWithNavigation)(TodoList)

Upvotes: 2

Related Questions