54

I'm using typescript in my react-native project(expo).

The project uses react-navigation, so on my screens I can set navigationOptions and I have access to the prop navigation.

Now I'm trying to strongly type these so I get hints for what properties are available to set.

interface NavStateParams {
    someValue: string
}

interface Props extends NavigationScreenProps<NavStateParams> {
   color: string
}

class Screen extends React.Component<Props, any> {
    // This works fine
    static navigationOptions: NavigationStackScreenOptions = {
        title: 'ScreenTitle'
    }
    // Does not work
    static navigationOptions: NavigationStackScreenOptions = ({navigation, screenProps }) => ({
        title: navigation.state.params.someValue
    })
}

What would be the best way to handle react-navigation as props for components.

Mellet
  • 1,196
  • 1
  • 10
  • 14

13 Answers13

47

Just add NavigationType to your Props, like this:

    import { StackNavigator, NavigationScreenProp } from 'react-navigation';

    export interface HomeScreenProps {
      navigation: NavigationScreenProp<any,any>
    };

    export class HomeScreen extends React.Component<HomeScreenProps, object> {

      render() {
        return (
          <View style={styles.container}>       
            <Button
              title="Go to Details"
              onPress={() => this.props.navigation.navigate('Details')}
            />
          </View>
        );
      }
    }
Andrej Kyselica
  • 846
  • 9
  • 8
  • But then I would have to provide the `navigation` props if I were to import the `HomeScreen` component from other components(say, `App.tsx`). How can I avoid it? – Searene Jul 30 '18 at 00:41
  • In your top component, use the withNavigation function to supply these props https://reactnavigation.org/docs/en/with-navigation.html – Andrej Kyselica Aug 01 '18 at 16:20
  • I haven't figured out a way to use `withNavigation` with typescript, either, because it also requires an extra navigation prop. I have another question about it. https://stackoverflow.com/questions/51585890/how-to-use-react-navigations-withnavigation-in-typescript – Searene Aug 02 '18 at 03:58
  • 1
    All this does is provide types for `props.navigation` within instance methods, which was already achieved in the original question. The question is asking 1) How to get types for `navigation` within the `navigationOptions` static method, and 2) How to get types for the parameters that are available in the navigation state. – collinksmith Aug 31 '18 at 00:03
  • After adding navigation to props as shown above I exported my component as `export default connect(mapStateToProps, mapDispatchToProps)(withNavigation(MyComponent));` and that resolved the typescript complaint that `MyComponent` was missing props – Syed Jafri Oct 23 '19 at 02:05
  • NavigationScreenProp is not supported in ver >= 6.x – Facundo Colombier Aug 18 '23 at 15:07
34

if you are passing the navigation prop that is defined by

let navigation = useNavigation()

to a component, the best way of typing is:

import {NavigationProp, ParamListBase} from '@react-navigation/native';

navigation: NavigationProp<ParamListBase>

Update:

Here is a better approach for strong navigation typing, using the latest @react-navigation version (6.x)

full example:

import {NativeStackNavigationProp} from '@react-navigation/native-stack';

type RootStackParamList = {

   ScreenOne: undefined; //current screen

   ScreenTwo: {slug: string}; // a screen that we are 
// navigating to, in the current screen,
// that we should pass a prop named `slug` to it

   ScreenThree: {data: Array<string>};

   ScreenFour: undefined; // a screen that we are navigating to 
// in the current screen, that we don't pass any props to it
};

interface IPageProps {
   navigation: NativeStackNavigationProp<RootStackParamList, 'ScreenOne'>;
}

// Since our screen is in the stack, we don't need to 
// use `useNavigation()` to provide the `navigation` to
// our component, we just need to read it as a prop

function Pdp({navigation}: IPageProps) {
   return ...
}
Andrew
  • 3,825
  • 4
  • 30
  • 44
Mahdieh Shavandi
  • 4,906
  • 32
  • 41
15

A minimal configuration, with version 6.x

import { NavigationProp } from "@react-navigation/native";

interface RouterProps {
    navigation: NavigationProp<any, any>;
}

<TouchableOpacity onPress={() => navigation.navigate('Home')}>
    <Text>Navigate to Home</Text>
</TouchableOpacity>
Osama Malik
  • 267
  • 2
  • 6
5

This works:

static navigationOptions = ({ navigation }: NavigationScreenProps) => ({
  ...
})
J. Hesters
  • 13,117
  • 31
  • 133
  • 249
4

I have same issue, and here's my solution:

import * as React from 'react'
import { NavigationScreenProps, NavigationStackScreenOptions } from 'react-navigation'

interface NavStateParams {
  someValue: string
}

// tslint:disable-next-line:no-any
type NavigationOptionsFn<TParams=any> = (props: NavigationScreenProps<TParams>) => NavigationStackScreenOptions

class Screen extends React.Component {
  // This should works fine
  static navigationOptions: NavigationOptionsFn<NavStateParams> = ({ navigation, screenProps }) => ({
    title: navigation.state.params.someValue
  })
}

You may want to declare NavigationOptionsFn<TParams> type into some d.ts file to make it work globally.

Adiono
  • 1,034
  • 11
  • 19
4

I think with react-navigation 5.X it's simpler now. Here is how to type hint navigation props passed to screens/components:

export default class Header extends React.Component<{
    navigation: StackNavigationHelpers;
}> {
...
}

Ps: Tested with these versions

"@react-navigation/native": "^5.2.3",
"@react-navigation/stack": "^5.3.1",
0case
  • 443
  • 7
  • 13
  • How do we invoke `navigation.setOptions()` in this case? It says `Property 'setOptions' does not exist on type 'StackNavigationHelpers'` – highjump Jul 30 '21 at 18:04
  • @highjump yeah correct, need to look for an alternate solution. – 0case Jul 31 '21 at 21:47
3
 yarn add --dev @types/jest @types/react-navigation

import { NavigationScreenProps } from "react-navigation";

export interface ISignInProps extends NavigationScreenProps<{}> { userStore: IUserStore }

export class SignInScreen extends React.Component { .... }
Javier González
  • 1,999
  • 17
  • 18
2
public static navigationOptions: NavigationScreenConfig<NavigationStackScreenOptions> = 
    ({navigation}) => ({/* Your options... */})
Labokas
  • 585
  • 5
  • 7
  • 1
    Its best solution for navigationOptions its support arrow function and object, thanks, also note use `NavigationScreenOptions` instead of `NavigationStackScreenOptions` – Ali Akbar Azizi Apr 21 '19 at 22:40
2

In case anyone is still experiencing this issue while extending NavigationScreenProps so you can correctly type navigationOptions etc along with your own props:

interface Props extends NavigationScreenProps {
  someProp: string;
  anotherProp: string;
}

export const SomeGreatScreen: NavigationScreenComponent<NavigationParams, {}, Props> = ({
  someProp,
  anotherProp,
}) => {
...
};

Whereas NavigationScreenComponent<Props> resulted in type errors for the destructured properties { someProp, anotherProp }, not recognizing the extension to the props, NavigationScreenComponent<NavigationParams, {}, Props> did. This appears to be due to the need to send the extended props type as the third parameter:

  export type NavigationScreenComponent<
    Params = NavigationParams,
    Options = {},
    Props = {}
  > = React.ComponentType<NavigationScreenProps<Params, Options> & Props> & {
    navigationOptions?: NavigationScreenConfig<Options>;
  };

from react-navigation.d.ts

Skyler
  • 91
  • 2
1

Rather than describing manually all your navigation functions (e.g: navigate), in your Props' interface, you can directly extends NavigationScreenProps.

In my case it was mandatory to stop eslint from yielding an error.

import { StackNavigator, NavigationScreenProps } from 'react-navigation';

export interface HomeScreenProps extends NavigationScreenProps {
/* your custom props here */
};

export class HomeScreen extends React.Component<HomeScreenProps, object> {

  render() {
    return (
      <View style={styles.container}>       
        <Button
          title="Go to Details"
          onPress={() => this.props.navigation.navigate('Details')}
        />
      </View>
    );
  }
}
Kruupös
  • 5,097
  • 3
  • 27
  • 43
1

A very simple solution

First add RootStackParamList type and PageProps interface to your navigator files

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import {
  createNativeStackNavigator,
  NativeStackNavigationProp,
} from '@react-navigation/native-stack';

import HomeScreen from './screens/Home';
import PasswordAddScreen from './screens/PasswordAdd';

export type RootStackParamList = {
  Home: undefined; // Add your props here
  PasswordAdd: undefined;
};

export interface PageProps<T extends keyof RootStackParamList> { // T is one of Home|PasswordAdd
  navigation: NativeStackNavigationProp<RootStackParamList, T>;
}

const Stack = createNativeStackNavigator<RootStackParamList>();

function Navigator() {
  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{ headerShown: false }}>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="PasswordAdd" component={PasswordAddScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default Navigator;

Then, in your Component or Screen file add

import React from 'react';
import { PageProps } from '../../Navigator';

function ItemsSeparator() {
  return <Divider my={1} bgColor="coolGray.50" />;
}

type Props = PageProps<'Home'>; // import and use the PageProps and pass the screen name - 
                                // exactly as it is in RootStackParamList

function HomeScreen({ navigation }: Props) {
  return (
    <Box safeArea bgColor="coolGray.100">
     ...
    </Box>
  );
}

export default HomeScreen;

Done.

Blatzo
  • 126
  • 2
  • 7
0

This seems to work:

public static navigationOptions: NavigationScreenOptionsGetter<
  NavigationScreenOptions
> = (navigation, stateProps) => ({
  title: navigation.state.params.someValue,
});
Simon Reggiani
  • 550
  • 10
  • 18
  • It seems the NavigationScreenOptionsGetter was made for this purpose, but it doesn't work. As you can see here: https://reactnavigation.org/docs/en/headers.html the navigationOptions function takes an object. – Sat Mandir Khalsa Aug 22 '18 at 19:28
0

The does not work section contains an error if your tsconfig.json has "strictNullChecks": true. In this case, you have an error indeed, because in the line

navigation.state.params.someValue

params is optional. What you could do is to check if the value was passed inside and provide default one otherwise, like:

title: navigation.state.params && navigation.state.params.someValue || 'Default title'

Vladyslav Zavalykhatko
  • 15,202
  • 8
  • 65
  • 100