reactjsreact-nativereact-navigationreact-native-tab-view

(react-native-tab-view) Using navigation.navigate in a TabBar?


I'm completely new to React Native and I'm building an app for one of our clients.

I created a screen that shows a list of names that can be shown in either alphabetical or grouped by category. I used the react-native-tab-view component to make this work. Each of the two tabs renders their own respective screen component, ScreenMapAlpha (shows a FlatList) and ScreenMapCategory (shows a SectionList).

For each renderItem prop of these lists, I wanted to make each item clickable using a TouchableOpacity into like a details page, however I couldn't get the navigation.navigate() function to work as I keep getting this message:

TypeError: undefined is not an object (evaluating '_this3.props.navigation.navigate')

I've been reading up on the react-native-tab-view Github documentation but I couldn't find a way to pass the navigation object into both of the Alpha/Category screen components for it to work.

My code looks like this:

import * as React from 'react';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import ScreenMapAlpha from '../screens/map_alpha';
import ScreenMapCategory from '../screens/map_cat';
import { TabView, TabBar, SceneMap } from 'react-native-tab-view';

const initialLayout = { width: Dimensions.get('window').width };

const renderTabBar = props => (
  <TabBar
    {...props}
    indicatorStyle={{ backgroundColor: '#fff' }}
    style={{ backgroundColor: '#c00' }}
  />
);

export default function ScreenMap({ navigation }) {
    const [index, setIndex] = React.useState(0);
    const [routes] = React.useState([
        { key: 'first', title: 'Alphabetical' },
        { key: 'second', title: 'Category' }
    ]);

    const renderScene = SceneMap({
        first: ScreenMapAlpha,
        second: ScreenMapCategory
    });

    return (
        <TabView
            navigationState={{index, routes}}
            renderScene={renderScene}
            onIndexChange={setIndex}
            renderTabBar={renderTabBar}
            initialLayout={initialLayout}
            navigation={navigation}
        />
    )  
}

map_alpha.js

import React, { useEffect, useState } from 'react';
import { StyleSheet, View, Text, FlatList, Image, TouchableOpacity } from 'react-native';
import TenantListAlpha from '../shared/tableTenantAlpha';

export default function ScreenMapAlpha({ navigation }) {       
    return (
        <View style={styles.container}>
            <TenantListAlpha navigation={navigation} />
        </View>
    )  
}

map_cat.js

import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import TenantListCat from '../shared/tableTenantCat';

export default function ScreenMapCategory({ navigation }) {
    return (
        <View style={styles.container}>
            <TenantListCat navigation={navigation} />
        </View>
    )  
}   

I'm using a StackNavigator for this app:

mapstack.js

import { createStackNavigator, createTopTabNavigator } from 'react-navigation-stack';
import Map from '../screens/map.js';
import React from 'react';
import CommonHeader from '../shared/header';
import TenantDetail from '../screens/tenant_detail';

const screens = {
    Map: {
        screen: Map,
        navigationOptions: ({ navigation }) => {
            return {
                headerTitle: () => <CommonHeader navigation={navigation} title="Directory" />
            }
        }
    },
    TenantDetail: {
        screen: TenantDetail,
        navigationOptions: ({ navigation }) => {
            return {
                //headerTitle: () => <CommonHeader navigation={navigation} title="News" />
                headerTitle: () => <Text>Directory</Text>
            }
        }
    }
}

const MapStack = createStackNavigator(screens);

export default MapStack;

I couldn't for the life of me figure this out - how do I pass the navigation object from the parent screen to the two screens in the tabs for navigation.navigate() to work?


Solution

  • It doesn't look like you're actually using react navigation. I would implement it, then navigation prop is passed to all your screens, you'll also be able to use the useNavigation() hook.

    https://github.com/satya164/react-native-tab-view#react-navigation

    https://reactnavigation.org/docs/tab-based-navigation

    Edit: I highly recommend implementing one of the react-navigation tab view wrappers, however you may be able to get your current code to work by properly passing the navigation prop.

    From the github page you have in your question: https://github.com/satya164/react-native-tab-view#renderscene-required

    If you need to pass additional props, use a custom renderScene function:
    
    const renderScene = ({ route }) => {
      switch (route.key) {
        case 'first':
          return <FirstRoute foo={this.props.foo} />;
        case 'second':
          return <SecondRoute />;
        default:
          return null;
      }
    };
    

    Which in your case would look like:

    const renderScene = ({ route }) => {
      switch (route.key) {
        case 'first':
          return <ScreenMapAlpha navigation={navigation} />;
        case 'second':
          return <ScreenMapCategory navigation={navigation} />;
        default:
          return null;
      }
    };