DEV Community

Andreas Bergqvist
Andreas Bergqvist

Posted on

React Navigation with Typescript

So.. You want to build an React Native app with Typescript and you have decided to use React Navigation as navigation library.

You have set all the Typescript stuff up and gets your app running!

You add React Navigation and all is fine!

But.. You add some screens that expects parameters and some that you want to edit the header and so on. There is not much information to find...

Here are some snippets how I have used React Navigation with Typescript.

Disclaimer

There are multiple ways of implementing typed React Navigation. These are only some examples and the naming of things should be considered to fit your application.

Any comments of other ways to solve it is appreciated!

First, classes vs functions...

I really enjoy using function components with hooks.
But currently there is an issue with hot reloading if using function components.
See: https://github.com/facebook/react-native/issues/10991

It just don't work. There might be solutions where you wrap you functional components around with classes, but hopefully this issue will soon be fixed!
https://mobile.twitter.com/dan_abramov/status/1125846420949434368

I will be using a useNavigation hook based of https://github.com/react-navigation/hooks.
But since the repo isn't very active, I just "stole" this function and modified abit:

import { useContext } from 'react';
import {
  NavigationScreenProp,
  NavigationRoute,
  NavigationContext,
} from 'react-navigation';

export function useNavigation<Params>() {
  return useContext(NavigationContext) as NavigationScreenProp<
    NavigationRoute,
    Params
  >;
}
Enter fullscreen mode Exit fullscreen mode

So, my examples will be with both classes and functional components.

Update header title and navigate

Class component

import React, { Component } from 'react';
import { Button, Text, View } from 'react-native';
import {
  NavigationParams,
  NavigationScreenProp,
  NavigationState,
} from 'react-navigation';

interface Props {
  navigation: NavigationScreenProp<NavigationState, NavigationParams>;
}

class TestScreen extends Component<Props> {
  public static navigationOptions = {
    title: 'Test Screen',
  };

  render() {
    const { navigation } = this.props;
    return (
      <View>
        <Text>Test Screen</Text>
        <Button
          title="Button"
          onPress={() => {
            navigation.navigate('anotherTestScreen');
          }}
        />
      </View>
    );
  }
}

export default TestScreen;
Enter fullscreen mode Exit fullscreen mode

Notice, that only screens that is set up directly on a navigation has the navigation property. If you would like a sub-component to have access to navigation you can do like this:

import React, { Component } from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationInjectedProps, withNavigation } from 'react-navigation';

class TestComponent extends Component<NavigationInjectedProps> {
  render() {
    const { navigation } = this.props;
    return (
      <Button
        title="Button"
        onPress={() => {
          navigation.navigate('anotherTestScreen');
        }}
      />
    );
  }
}

export default withNavigation(TestComponent);
Enter fullscreen mode Exit fullscreen mode

Function component

import React from 'react';
import { Button, Text, View } from 'react-native';
import { useNavigation } from '../hooks/useNavigation';

const AnotherTestScreen = () => {
  const navigation = useNavigation();
  return (
    <View>
      <Text>Test Screen</Text>
      <Button
        title="Button"
        onPress={() => {
          navigation.navigate('paramScreen', { text: 'Hi!' });
        }}
      />
    </View>
  );
};

AnotherTestScreen.navigationOptions = {
  title: 'Another Test Screen',
};

export default AnotherTestScreen;
Enter fullscreen mode Exit fullscreen mode

Typed params for the screens

Class component

import React, { Component } from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationScreenProp, NavigationState } from 'react-navigation';

interface NavigationParams {
  text: string;
}

type Navigation = NavigationScreenProp<NavigationState, NavigationParams>;

interface Props {
  navigation: Navigation;
}

class ParamScreen extends Component<Props> {
  public static navigationOptions = ({
    navigation,
  }: {
    navigation: Navigation;
  }) => ({
    title: navigation.state.params ? navigation.state.params.text : '',
  });

  render() {
    const { navigation } = this.props;
    const {
      state: { params },
    } = navigation;
    return (
      <View>
        <Text>Param: {params ? params.text : ''}</Text>
        <Button
          title="Button"
          onPress={() => {
            navigation.navigate('anotherParamScreen', { text: 'Hello!' });
          }}
        />
      </View>
    );
  }
}

export default ParamScreen;
Enter fullscreen mode Exit fullscreen mode

Why would you bother with that much extra typings code you might ask yourself? Why not just use any?
Well, this example might not be the best, but the params are now typed and you can get intellisense help in your editor:

Function component

import React from 'react';
import { Button, Text, View } from 'react-native';
import {
  NavigationScreenProp,
  NavigationState,
  StackActions,
  NavigationActions,
} from 'react-navigation';
import { useNavigation } from '../hooks/useNavigation';

interface NavigationParams {
  text: string;
}

type Navigation = NavigationScreenProp<NavigationState, NavigationParams>;

const AnotherParamScreen = () => {
  const navigation = useNavigation<NavigationParams>();
  const {
    state: { params },
  } = navigation;
  return (
    <View>
      <Text>Param: {params ? params.text : ''}</Text>
      <Button
        title="Button"
        onPress={() => {
          const resetAction = StackActions.reset({
            index: 0,
            actions: [NavigationActions.navigate({ routeName: 'testScreen' })],
          });
          navigation.dispatch(resetAction);
        }}
      />
    </View>
  );
};

AnotherParamScreen.navigationOptions = ({
  navigation,
}: {
  navigation: Navigation;
}) => ({
  title: navigation.state.params ? navigation.state.params.text : '',
});

export default AnotherParamScreen;

Enter fullscreen mode Exit fullscreen mode

Discussion (9)

Collapse
dhavaljardosh profile image
Dhaval Jardosh

Any articles to use createStackNavigator or createAppContainer?
I'm stuck here.

stackoverflow.com/questions/574406...

Collapse
cutiko profile image
Erick Navarro

This is amazing! I have been struggling with types and screen navigation props, this solved it, thanks

Collapse
andreasbergqvist profile image
Andreas Bergqvist Author

Awesome!

Collapse
madeinquant profile image
madeinquant

Can I find the source code of this tutorial?

Collapse
andreasbergqvist profile image
Andreas Bergqvist Author

Hi, currently no.. The code snippets are bits and pieces from a closed source project. Might share something later on.. But not planned.

Collapse
madeinquant profile image
madeinquant

Thank you

Collapse
mauroca80 profile image
mauroca80

great!!!!!

Collapse
anjueappen profile image
Anju Eappen

thanks for this! I was wondering where the navigation props referenced in the docs were concretely defined. Solved my issue as well.

Collapse
andreasbergqvist profile image
Andreas Bergqvist Author

Glad I could help!