React Navigation with Typescript
所以……你想用 Typescript 构建一个 React Native 应用,并且决定使用 React Navigation 作为导航库。
你已经完成了所有 TypeScript 的配置,并且你的应用程序可以运行了!
添加 React Navigation 后一切就都好了!
但是……你添加了一些需要参数的屏幕,还有一些屏幕需要编辑标题等等。这方面的信息并不多……
以下是我如何使用 React Navigation 和 TypeScript 的一些代码片段。
免责声明
实现类型化 React 导航的方法有很多种。这里仅列举一些示例,具体命名应根据你的应用程序进行调整。
欢迎提出其他解决方法!
首先,类和函数的区别……
我非常喜欢使用带有 Hooks 的函数组件。
但目前使用函数组件时存在热重载问题。
参见:https://github.com/facebook/react-native/issues/10991
它就是行不通。或许可以用类来封装函数式组件,但希望这个问题能尽快得到解决!
https://mobile.twitter.com/dan_abramov/status/1125846420949434368
我将使用基于https://github.com/react-navigation/hooks的 useNavigation hook 。
但由于该仓库不太活跃,我只是“借用”了这个函数并稍作修改:
import { useContext } from 'react';
import {
NavigationScreenProp,
NavigationRoute,
NavigationContext,
} from 'react-navigation';
export function useNavigation<Params>() {
return useContext(NavigationContext) as NavigationScreenProp<
NavigationRoute,
Params
>;
}
因此,我的示例将同时包含类组件和函数式组件。
更新标题和导航
类组件
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;
请注意,只有直接设置在导航栏上的屏幕才具有 navigation 属性。如果您希望子组件能够访问 navigation,可以这样做:
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);
功能组件
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;
屏幕的输入参数
类组件
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;
你可能会问,为什么要费劲写这么多额外的类型定义代码呢?为什么不直接用 ` ?` 呢any?
好吧,这个例子可能不是最好的,但现在参数都已类型化,你可以在编辑器中获得智能提示:
功能组件
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;
