2

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 Answers 2

2

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;
}
/* ... */
Sign up to request clarification or add additional context in comments.

Comments

1

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)!;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.