2
votes

Here is my navigator. I have a bottomTab with 3 stack navigators as screens 'Home' 'Profile' 'Discover', and main root stack navigator with bottom tab navigator and few modal screens. I can not understand how to do Type checking with TypeScript right from guide in docs. Can someone explain me what am i doing wrong. For right now if i want to navigate from tab Home to tab Profile i can see only to 'Home' and all modal screens and to tabNavigator itself but not to tabs of my tabNavigator:

enter image description here

my code of mainNavigator:

export type TabParamList = {
  Home: undefined;
  Discover: undefined;
  Profile: undefined;
}

export type MainStackParamList = {
  TabNavigator: undefined;
  ModalStreamsPlayer: { streamsQualitys: ParsedStream[], stream: Stream} | undefined;
  ModalWebBrowser: { url: string, title: string } | undefined;
  ModalVideoPlayer: { video: Video } | undefined;
}

export type HomeStackParamList = {
  Home: undefined;
}

export type DiscoverStackParamList = {
  Discover: undefined;
}

export type ProfileStackParamList = {
  Profile: undefined;
}

const Tab = createBottomTabNavigator<TabParamList>();
const RootStack = createStackNavigator<MainStackParamList>();
const HomeStack = createStackNavigator<HomeStackParamList>();
const DiscoverStack = createStackNavigator<DiscoverStackParamList>();
const ProfileStack = createStackNavigator<ProfileStackParamList>();

const screenOptions = ({ route }): StackNavigationOptions => {
  return {
    title: route.name,
    headerTitleStyle: {
      fontFamily: 'ProximaNova-Semibold',
      fontSize: 18,
      textTransform: 'uppercase',
      lineHeight: 22,
      color: '#D6FFFD'
    },
    headerStyle: {
      backgroundColor: '#133740',
      elevation: 0,
      shadowOpacity: 0,
      borderBottomWidth: 0
    },
    headerTintColor: '#D6FFFD',
    headerTitleAlign: 'center',
  }
}

const TabHomeStack = () => {
  return (
    <HomeStack.Navigator>
      <HomeStack.Screen
        name='Home'
        component={HomeScreen}
        options={screenOptions}
      />
    </HomeStack.Navigator>
  );
};

const TabDiscoverStack = () => {
  return (
    <DiscoverStack.Navigator>
      <DiscoverStack.Screen
        name='Discover'
        component={DiscoverScreen}
        options={screenOptions}
      />
    </DiscoverStack.Navigator>
  );
}

const TabProfileStack = () => {
  return (
    <ProfileStack.Navigator>
      <ProfileStack.Screen
        name='Profile'
        component={ProfileScreen}
        options={screenOptions}
      />
    </ProfileStack.Navigator>
  )
}

const TabNavigator = () => {
  return (
    <Tab.Navigator
      tabBarOptions={{
        showLabel: false,
        // activeTintColor: '#2F80ED',
        // inactiveTintColor: '#999999',
        style: {
          backgroundColor: '#133740',
          height: Platform.OS === 'ios' ? 94 : 60,
          borderTopWidth: 0
        }
      }}>
      <Tab.Screen
        name='Home'
        component={TabHomeStack}
        options={{ tabBarIcon: ({color, focused, size}) => (
          <Image
          source={ focused ? images.tabBarIconHomeActive : images.tabBarIconHomeNormal } 
          />
        )}}
      />
      <Tab.Screen
        name='Discover'
        component={TabDiscoverStack}
        options={{ tabBarIcon: ({color, focused, size}) => (
          <Image
          source={ focused ? images.tabBarIconDiscoverActive : images.tabBarIconDiscoverNormal } 
          />
        )}}
      />
      <Tab.Screen
        name='Profile'
        component={TabProfileStack}
        options={{ tabBarIcon: ({color, focused, size}) => (
          <Image
          source={ focused ? images.tabBarIconProfileActive : images.tabBarIconProfileNormal } 
          />
        )}}
      />
    </Tab.Navigator>
  );
};

const RootStackNavigator = () => {
  return (
    <RootStack.Navigator mode='modal'>
      <RootStack.Screen
        name='TabNavigator'
        component={TabNavigator}
        options={{ headerShown: false }}
      />
      <RootStack.Screen
        name='ModalStreamsPlayer'
        component={StreamsPlayer}
        options={{ headerShown: false }}
      />
      <RootStack.Screen
      name='ModalWebBrowser'
      component={WebScreen}
      options={{ headerShown: false }}
      />
      <RootStack.Screen
        name='ModalVideoPlayer'
        component={YoutubePlayer}
        options={{ headerShown: false }}
      />
    </RootStack.Navigator>
  );
}

export default RootStackNavigator;

Home screen from Home tab:

type HomeScreenNavigationProp = CompositeNavigationProp<
  BottomTabNavigationProp<HomeStackParamList, 'Home'>,
  StackNavigationProp<MainStackParamList>
>;

type HomeScreenRouteProp = RouteProp<
  HomeStackParamList, 'Home'
>;

export type HomeProps = {
  navigation: HomeScreenNavigationProp;
  route: HomeScreenRouteProp;
};

export default class HomeScreen extends React.Component<HomeProps> {
  render() {
    return (
      <View style={styles.container}>
        <ScrollView style={styles.scrollView}>
          <View style={{ paddingBottom: 24 }}>
            <StreamsScreen navigation={this.props.navigation} />
            <View style={styles.separator}></View>
            <NewsScreen navigation={this.props.navigation} />
            <View style={styles.separator}></View>
            <VideosScreen navigation={this.props.navigation} />
            <View style={styles.separator}></View>
          </View>
        </ScrollView>
      </View>
    );
  }
}
2

2 Answers

0
votes

You can refer to this: https://dev-yakuza.github.io/en/react-native/react-navigation-v5/ In this reference, source codes are provided too. https://github.com/dev-yakuza/react-navigation-v5-exercise

Here are the steps of my case which is not the same as the above reference:

  1. define a type for navigation params Here are two screens in profile stack e.g.
// profile stack params list
export type ProfileStackParams = {
  Profile: undefined;
  ProfileEdit: undefined;
};

the undefined means here that the params are not defined. You can specify your params if necessary.

  1. define a navigation route prop e.g. using the ProfileStackParams, define a route params:
export type ProfileRouteProp = RouteProp<ProfileStackParams, 'Profile'>;

or you can define a navigation props:

export type ProfileNavigationProp = StackNavigationProp<ProfileStackParams, 'Profile'>;
  1. setup profile stack of bottom tap navigator
// stack navigator
const Stack = createStackNavigator();
// bottom tab navigator, here I use material bottom tab navigator
//const Tab = createBottomTabNavigator<BottomTabParams>();
const Tab = createMaterialBottomTabNavigator<BottomTabParams>();

Then, setup profile stack and other stacks.

const TabProfileStack = (props): JSX.Element => {
  console.log('TabProfileStack props', props);

  return (
    <Stack.Navigator mode="card" headerMode="screen">
      <Stack.Screen
        name="Profile"
        component={Profile}
      />
      <Stack.Screen name="ProfileEdit" component={ProfileEdit} />
    </Stack.Navigator>
  );
};
  1. setup bottom navigator
const TabNavigator = (props) => {
  return (
    <Tab.Navigator
      initialRouteName="Profile"
      labeled={true}
    >
      <Tab.Screen
        name="Feed"
        component={TabFeedStack}
      />
      <Tab.Screen
        name="Profile"
        component={TabProfileStack}
      />
    </Tab.Navigator>
  );
};

One problem of the tab navigator is that you cannot pass params to the child screen of tab navigator.

In this case, you can use React.useContext to set/get params. Because of this, my Profile component does not take any props.

const ProfileScreen = (): JSX.Element => {
  // use context instead of navigation params 
  const {authState} = useContext(AuthContext)!;
0
votes

You need to use twice CompositeNavigationProp in your screen and NavigatorScreenParams for the nesting navigators, (https://reactnavigation.org/docs/typescript#combining-navigation-props) this seem a common pattern to use a modal, but docs lack.

in your navigator :

import { NavigatorScreenParams } from "@react-navigation/native";
/* ... */
export type TabParamList = {
  Home: undefined;
  Discover: undefined;
  Profile: undefined;
}

export type MainStackParamList = {
  TabNavigator: NavigatorScreenParams<TabParamList>;
  ModalStreamsPlayer: {...};
  ModalWebBrowser: {...};
  ModalVideoPlayer: {...};
}
/* ... */

NB: if you use a StackNavigator for each tab, use NavigatorScreenParams for each one

and in your screen:

/* ... */
type PrimaryNavigator = StackNavigationProp<HomeStackParamList, "Home">;
type PrimaryNavigatorParent = BottomTabNavigationProp<TabParamList>;
type RootNavigatorParent = BottomTabNavigationProp<MainStackParamList>;
type PrimaryNavigatorNested = CompositeNavigationProp<PrimaryNavigator, RootNavigatorParent>;

type HomeProps = {
  navigation: CompositeNavigationProp<PrimaryNavigatorNested, PrimaryNavigatorParent>;
  route: HomeScreenRouteProp;
}
/* ... */