Search code examples
react-nativereact-navigationreact-navigation-stackreact-navigation-bottom-tabreact-navigation-v6

Type error in getting route params within nested navigator using Typescript [react-navigation v6]


Having a navigation type definition as bellow, when I navigate from e.g AOne to BTwo with id:99 the console log of props.route.params shows correct info. But props.route.params.id throws type error

TypeError: undefined is not an object (evaluating 'props.route.params.id')

    // navigation related imports in all components 
    import {BottomTabScreenProps} from '@react-navigation/bottom-tabs';
    import {CompositeScreenProps, NavigatorScreenParams} from '@react-navigation/core';
    import {StackScreenProps} from '@react-navigation/stack';

    // type defenitions
    export type StackOneParams = {
      AOne:undefined,
      BOne: undefined,
      // some other screens
    };
    export type StackTwoParams = {
      ATwo: undefined;
      BTwo:{id:number};
      // some other screens
    };
    
    export type TabParams = {
      StackOne: NavigatorScreenParams<StackOneParams>;
      StackTwo: NavigatorScreenParams<StackTwoParams>;
    // ... some other stacks each represent a tab 
    };

    export type RootParamList = {
       ROne: undefined;  // these screens should stand alone and not blong to any tab
       RTwo: undefined;
       Tabs: NavigatorScreenParams<TabParams>
    }

    // navigation from AOne to BTwo
      props.navigation.navigate('Tabs', {
         screen: 'StackTwo',
         params: {screen: 'BTwo', params: {id: 99}}
     }); // this part give correct auto complete hints in VSCode and no compilation error

 // BTwo component (screen)
//--------------------------------
    type Props = CompositeScreenProps<
  StackScreenProps<RootParamList, 'Tabs'>,
  CompositeScreenProps<
    BottomTabScreenProps<TabPrams, 'StackTwo'>,
    StackScreenProps<StackTwoParams, 'BTwo'>
  >
>;// using CompositeScreenProps to be able to navigate to screens in another tabs
// otherwise just `type Props=StackScreenProps<StackTwoParams, 'BTwo'>` works fine but cannot navigate to other tabs


    const BTwo:React.FC<Props> = (props) =>{
    console.log(props.route.params)// the log shows {id:23} 
    // but props.route.params.id gives error and also no auto completion hint 
       return(...)
    }
  • is this the correct way to define screen props for a specific screen, like what I have in BTwo screen? or the sequence of the composition should be different?

most of the examples (and the official documentation) show the most simple composition where the target screen is not even in second level of nesting (in the official doc profile is not really in nested bottom tabs)

How should I solve the type error in this case?

the image shows the VSCode auto-complete suggestions

enter image description here


Solution

  • Solution using CompositeScreenProps

    My other explanations were not quite accurate. The way you have defined the CompositeScreenProp is not correct. Here is the correct way to implement this.

    type ScreenProps = CompositeScreenProps<
      StackScreenProps<StackTwoParams, "BTwo">,
      CompositeScreenProps<
        BottomTabScreenProps<TabParams, "StackTwo">, 
        StackScreenProps<RootParamList>
      >
    >
    

    The first parameter of CompositeScreenProps contains the type of the navigator that owns the screen. In this case BTwo is owned by StackTwo and this determines the primary navigator, which is a Stack.

    The above yields to the correct types as well.

    const BTwo = (props: ScreenProps) => {
      return <></>
    }
    

    enter image description here

    enter image description here


    Solution using separate types for navigation and route

    We can type the navigation object and the route object separately as follows.

    type NavigationProps = CompositeNavigationProp<
      StackNavigationProp<StackTwoParams, "BTwo">,
      CompositeNavigationProp<
        BottomTabNavigationProp<TabParams, "StackTwo">, 
        StackNavigationProp<RootParamList>
      >
    >
    
    type ScreenPropsA = {
      navigation: NavigationProps
      route: RouteProp<StackTwoParams, "BTwo">
    }
    

    Notice the usage of CompositeNavigationProp and RouteProp here.

    Then, use it as follows.

    
    const BTwo = ({ route, navigation }: ScreenProps) => {
    
      return <></>
    }
    

    Both, route and navigation are now correctly typed.

    enter image description here

    enter image description here

    enter image description here