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
>;
}
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;
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);
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;
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;
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;
Top comments (9)
Any articles to use createStackNavigator or createAppContainer?
I'm stuck here.
stackoverflow.com/questions/574406...
This is amazing! I have been struggling with types and screen navigation props, this solved it, thanks
Awesome!
Can I find the source code of this tutorial?
Hi, currently no.. The code snippets are bits and pieces from a closed source project. Might share something later on.. But not planned.
Thank you
great!!!!!
thanks for this! I was wondering where the navigation props referenced in the docs were concretely defined. Solved my issue as well.
Glad I could help!