TypeScript在React Native中的实战
上一篇文章中我们已经配置好TypeScript和文件绝对路径的查找. 接下来我们将实战使用Typescript.
我们稍微扩展一下项目, 首先在src/pages/
下新建Home.tsx
, Detail.tsx
, Found.tsx
, Listen.tsx
, Account.tsx
, 然后在src/navigator
下新建index.tsx
.
接着我们安装React Native导航器来管理路由导航. 具体安装参考官方文档react-navigation官方文档.安装配置完成后我们继续完善应用, 使页面可以跳转传参.
假设我们的应用有以下两个需求
- 四个Tab的BottomTab, 点击不同的tab渲染对应的标题.
- 点击
Home
的中按钮跳转到Detail
页面并传参.
我们先配置路由, 修改navigator/index.tsx
:
import {NavigationContainer} from '@react-navigation/native'
import {createStackNavigator} from '@react-navigation/stack
const Stack = createStackNavigator()
class Navigator extends React.Component {
render () {
return (
<NavigationContainer>
<Stack.navigator>
<Stack.Screen name="BottomTabs" component={BottomTabs} />
<Stack.Screen name="Detail" component={Detail} />
</Stack.Navigator>
</NavigationContainer>
)
}
}
export default Navigator
@react-navigation/stack
堆栈导航器. 我们让堆栈导航器包裹标签导航器(BottomTabs), 以便在BottomTabs
中直接渲染Home
组件. 同样的点击不同的Tab时渲染对应的标题.
同样的我们也可以让标签导航器包裹堆栈导航器, 不过这中方法会导致我们点击不同的Tab时渲染的都是同样的标题.
import {StackNavigationProp} from '@react-navigation/stack
......
type RootStackParamsList = {
BottomTabs: {
screen?: String
},
Detail: {
id: String
}
}
export type RootStackNavigation = StackNavigationProp<RootStackParamsList>
const Stack = createStackNavigator<RootStackParamsList>()
class Navigator extends React.Component {
...
}
我们声明了一个RootStackParamsList
, 它包含我们要渲染的BottomTabs
和Detail
组件, 这两个组件都需要接受一个String类型的参数
同时我们实例化createStackNavigation
, 实例化后的Stack
是RootStackParamsList
类型, 这样我们才能推断出渲染什么组件.
另外的我们导出了一个RootStackNavigation
, 它同样是RootStackParamsList
类型, 具体可以查看官方文档type-checking-screens
接下来我们完善BottomTabs
:
import {RouteProp, TabNavigationState} from '@react-navigation/core'
import {getFocusedRouteNameFromRoute} from '@react-navigation/native'
import {createBottomNavigator} from '@react-navigation/bottom-tabs'
import {RootStackNavigation, RootStackParamsList} from './index'
export type BottomTabParamsList = {
Home: undefined,
Listen: undefined,
Found: undefined,
Account: undefined
}
type Route = RouteProp<RootStackParamsList, 'BottomTabs'> & {
state?: TabNavigationState
}
export interface IProps {
navigation: RootStackNavigation,
route: Route
}
const Tabs = createBottomNavigator<BottomTabsParamsList>()
fucntion getHeaderTitle(route: Route) {
const routeName = getFocusedRouteNameFromRoute(route)
switch(routeName) {
case 'Home':
return '首页'
case 'Listen':
return '我听'
case 'Found':
return '发现'
case 'Account':
return '我的'
default:
return '首页'
}
}
class BottomTabs extends React.Component<IProps> {
componentDidUpdate () {
const {navigation, route} = this.props
navigation.setOptions({
headeTitle: getHeaderTitile(route)
})
}
render () {
return (
<Tabs.Navigator>
<Tabs.screen
name="Home"
component={Home}
/>
<Tabs.screen
name="Found"
component={Found}
options={{
tabBarLabel: '发现'
}}
/>
<Tabs.screen
name="Listen"
component={Listen}
options={{
tabBarLabel: '我听'
}}
/>
<Tabs.screen
name="Account"
component={Account}
options={{
tabBarLabel: '我的'
}}
/>
</Tabs.Navigator>
)
}
}
当我们点击Tab时会触发componentDidUpdate
这个生命周期, 在此生命周期内可以为当前Tab设置页面标题.
我们定义了一个方法getHeadertitle
, 在该方法内用getFocusedRouteNameFromRoute
获取当前激活的路由的name
. getHeaderTitle
接收一个Route
类型, 该类型是一个交叉类型, 它既包含RouteProp
也就是路由本身特性又包含TabNavigationState
的特性.
到此BottomTabs
就完成了. 接下来我们完成页面跳转并传参, 修改Home.tsx
:
import {RootStackNavigation} from '../navigator/index'
interface IProps {
navigation: RootStackNavigation
}
class Home extends React.Component<IProps> {
onPress = () => {
const {navigation} = this.props
navigation.navigate('Detail', {
id: 'sfasf'
})
}
render () {
return (
<View>
<Button title='跳转到Detail' onPress={this.onPress}></Button>
</View>
)
}
}
修改Detail.tsx
import {RouteProp} from '@react-navigation/core'
import {RootStackParamsList} from '../navigator/index'
interface IProps {
route: RouteProp<RootStackParamsList, 'Detail'>
}
class Detail extends React.Component<IProps> {
render () {
const {route} = this.props
return (
<View>
<Text>{route.params?.id}</Text>
</View>
)
}
}
Detail
通过route
来获取传递过来的参数id
, 所以route
应该是RouteProp
类型. 最后通过route.params.id
来获取参数.