React-Navigation使用
React Native
原生应用导航框架,更接近原生的体验
React-Navigation
React-Native
官方推荐使用 React-Navigation 来实现路由跳转。
1. 安装依赖包
- @react-navigation/native: 核心依赖
- @react-navigation/native-stack: 栈式导航
- @react-navigation/elements: 元素 (Text, Button)
- react-native-screens react-native-safe-area-context: 兼容依赖
# 核心依赖
npm install @react-navigation/native
# 安装 expo
npm install expo
# expo 安装 react-native-screens 和 react-native-safe-area-context
npx expo install react-native-screens react-native-safe-area-context
# 安装兼容依赖
npm install react-native-screens react-native-safe-area-context
# 原生导航依赖
npm install @react-navigation/native-stack
# 元素依赖
npm install @react-navigation/elements
# 核心依赖
pnpm add @react-navigation/native
# 安装 expo
pnpm add expo
# expo 安装 react-native-screens 和 react-native-safe-area-context
pnpm dlx expo install react-native-screens react-native-safe-area-context
# 安装兼容依赖
pnpm add react-native-screens react-native-safe-area-context
# 原生导航依赖
pnpm add @react-navigation/native-stack
# 元素依赖
pnpm add @react-navigation/elements
# 核心依赖
yarn add @react-navigation/native
# 安装 expo
yarn add expo
# expo 安装 react-native-screens 和 react-native-safe-area-context
yarn dlx expo install react-native-screens react-native-safe-area-context
# 安装兼容依赖
yarn add react-native-screens react-native-safe-area-context
# 原生导航依赖
yarn add @react-navigation/native-stack
# 元素依赖
yarn add @react-navigation/elements
# 核心依赖
bun add @react-navigation/native
# 安装 expo
bun add expo
# expo 安装 react-native-screens 和 react-native-safe-area-context
bunx expo install react-native-screens react-native-safe-area-context
# 安装兼容依赖
bun add react-native-screens react-native-safe-area-context
# 原生导航依赖
bun add @react-navigation/native-stack
# 元素依赖
bun add @react-navigation/elements
# 核心依赖
deno add @react-navigation/native
# 安装 expo
deno add expo
# expo 安装 react-native-screens 和 react-native-safe-area-context
deno run -A expo install react-native-screens react-native-safe-area-context
# 安装兼容依赖
deno add react-native-screens react-native-safe-area-context
# 原生导航依赖
deno add @react-navigation/native-stack
# 元素依赖
deno add @react-navigation/elements
1.1 修改 MainActivity.kt
需要进行此更改,以避免与 View 状态在 Activity 重新启动期间未一致持久相关的崩溃。
在 /android/app/src/packageName/MainActivity.java 中添加以下代码
class MainActivity: ReactActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
}
// ...
}
2. 动态配置路由
添加 NavigationContainer
导航容器,这是必须的
import * as React from "react"
import { NavigationContainer } from "@react-navigation/native"
export default function App() {
return (
<NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
)
}
2.1 添加路由
使用 createNativeStackNavigator()
可以创建一个导航器。
createNativeStackNavigator
是一个返回包含 2 个属性的对象的函数:Screen
和 Navigator
。 它们都是用于配置导航器的 React
组件。Navigator
应包含 Screen
元素作为其子元素来定义路由的配置。
// In App.js in a new project
import * as React from "react"
import { View, Text } from "react-native"
import { NavigationContainer } from "@react-navigation/native"
import { createNativeStackNavigator } from "@react-navigation/native-stack"
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Home Screen</Text>
</View>
)
}
const Stack = createNativeStackNavigator()
function RootStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<RootStack />
</NavigationContainer>
)
}
2.2 添加第二个路由
通过属性 initialRouteName
可以设置初始路由。
function RootStack() {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
)
}
传递额外的参数
<Stack.Screen name="Home">
{(props) => <HomeScreen {...props} extraData={someData} />}
</Stack.Screen>
2.3 配置路由参数
Stack.Screen
组件接受一个 options
属性,该属性是一个对象,其中包含路由配置。
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: "Overview" }}
/>
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
3. 路由跳转
使用 useNavigation
钩子来获取导航器实例。
import * as React from "react"
import { View, Text } from "react-native"
import { createStaticNavigation, useNavigation } from "@react-navigation/native"
import { createNativeStackNavigator } from "@react-navigation/native-stack"
import { Button } from "@react-navigation/elements"
function HomeScreen() {
const navigation = useNavigation()
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Home Screen</Text>
<Button onPress={() => navigation.navigate("Details")}>
Go to Details
</Button>
</View>
)
}
// ... other code from the previous section
提示
当前由于没有设置 TypeScript 类型, 所以以上代码在 Ts 项目将会报错,不过不会影响程序正常运行。
3.1 push
导航
如果使用 navigate
方法进行导航,如果当前页已经是该导航,则不会进行导航。 此时,应该使用 push
方法。
<Button onPress={() => navigation.push("Details")}>
Go to Details... again
</Button>
3.2 goBack
导航
goBack
方法用于返回上一个页面。
function DetailsScreen() {
const navigation = useNavigation()
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Details Screen</Text>
<Button onPress={() => navigation.push("Details")}>
Go to Details... again
</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
)
}
3.3 popTo
popTo
方法用于返回到指定页面。另外 popToTop 方法用于返回到栈顶。
function DetailsScreen() {
const navigation = useNavigation()
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Details Screen</Text>
<Button onPress={() => navigation.push("Details")}>
Go to Details... again
</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
<Button onPress={() => navigation.popTo("Home")}>Go to Home</Button>
<Button onPress={() => navigation.popToTop()}>
Go back to first screen in stack
</Button>
</View>
)
}
3.4 路由传参
1.通过将参数放入对象中作为 navigation.navigate
函数的第二个参数来将参数传递给路由:navigation.navigate('RouteName', { /_ params go here _/ })
2.读取屏幕组件中的参数:route.params
。
function HomeScreen() {
const navigation = useNavigation()
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Home Screen</Text>
<Button
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate("Details", {
itemId: 86,
otherParam: "anything you want here",
})
}}
>
Go to Details
</Button>
</View>
)
}
function DetailsScreen({ route }) {
const navigation = useNavigation()
/* 2. Get the param */
const { itemId, otherParam } = route.params
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Details Screen</Text>
<Text>itemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
<Button
onPress={() =>
navigation.push("Details", {
itemId: Math.floor(Math.random() * 100),
})
}
>
Go to Details... again
</Button>
<Button onPress={() => navigation.navigate("Home")}>Go to Home</Button>
<Button onPress={() => navigation.goBack()}>Go back</Button>
</View>
)
}
3.4.1 设置初始参数
<Stack.Screen
name="Details"
component={DetailsScreen}
initialParams={{ itemId: 42 }}
/>
3.4.2 修改参数
使用 setParams
动态修改参数
navigation.setParams({
itemId: Math.floor(Math.random() * 100),
})
3.4.3 将参数传递到上一个屏幕
function HomeScreen({ route }) {
const navigation = useNavigation()
// Use an effect to monitor the update to params
React.useEffect(() => {
if (route.params?.post) {
// Post updated, do something with `route.params.post`
// For example, send the post to the server
alert("New post: " + route.params?.post)
}
}, [route.params?.post])
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Button onPress={() => navigation.navigate("CreatePost")}>
Create post
</Button>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</View>
)
}
function CreatePostScreen({ route }) {
const navigation = useNavigation()
const [postText, setPostText] = React.useState("")
return (
<>
<TextInput
multiline
placeholder="What's on your mind?"
style={{ height: 200, padding: 10, backgroundColor: "white" }}
value={postText}
onChangeText={setPostText}
/>
<Button
onPress={() => {
// Pass params back to home screen
navigation.popTo("Home", { post: postText })
}}
>
Done
</Button>
</>
)
}
3.4.4 将参数传递到嵌套屏幕
navigation.navigate("More", {
screen: "Settings",
params: { user: "jane" },
})
4. 配置 Header
动态设置标题
<Button onPress={() => navigation.setOptions({ title: "Updated!" })}>
Update the title
</Button>
在标题中使用参数
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: "My home" }}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={({ route, navigation }) => ({
title: route.params.name,
})}
/>
</Stack.Navigator>
)
}
4.1 跨屏共享 options
使用 screenOptions
同时设置多个子 screen
的options
。
function MyStack() {
return (
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: "#f4511e",
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "bold",
},
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: "My home" }}
/>
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
)
}
4.2 自定义标题
function LogoTitle() {
return (
<Image
style={{ width: 50, height: 50 }}
source={require("@expo/snack-static/react-native-logo.png")}
/>
)
}
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerTitle: (props) => <LogoTitle {...props} />,
}}
/>
</Stack.Navigator>
)
}
5. 配置 TypeScript
通常将类型定义文件放在 /type/navigation/types.tsx
目录下,方便管理。
- 常用的类型
RootStackParamList
: 导航栈配置(自定义),使用导航时,可以获取到导航名称和导航参数类型NativeStackNavigationProp<RootStackParamList, 'Name'>
: 导航参数类型,用于导航跳转时提供 ts 类型NativeStackScreenProps<RootStackParamList, 'Name', 'id'>
: 导航屏幕接收参数类型,用于导航目的地接收参数,包含:['navigation', 'route'] 两个对象NativeStackScreenProps<RootStackParamList, 'Name'>['navigation'] = NativeStackNavigationProp<RootStackParamList, 'Name'>
NativeStackScreenProps<RootStackParamList, 'Name'>['route'] = RouteProp<RootStackParamList, 'Name'>
NavigatorScreenParams<ChildParamList>
: 用于定义嵌套导航的子集
5.1 配置 ParamList
配置了 ParamList 后,在导航跳转时,可以获取到导航名称和导航参数类型。
export type RootStackParamList = {
/**
* 主路由栈配置
*/
Main: NavigatorScreenParams<MainStackParamList>
Login: undefined
/**
* 组件列表
*/
Component: undefined
} & ComponentParamList
export type MainStackParamList = {
Home: {
post: string
}
Mine: {
content: string
}
}
export type ComponentParamList = {
MyView: undefined
MyText: undefined
MyButton: undefined
MySwitch: undefined
}
使用 ParamList
/**
* 根栈
*/
const RootStack = createNativeStackNavigator<RootStackParamList>()
/**
* 主栈
*/
const MainStack = createBottomTabNavigator<MainStackParamList>()
<NavigationContainer theme={isDarkMode ? DarkTheme : DefaultTheme}>
<RootStack.Navigator initialRouteName="Main">
<RootStack.Screen name="Main" component={MainStackScreen} />
</RootStack.Navigator>
</NavigationContainer>
5.2 配置 ScreenProps
配置通用屏幕的 Props
类型
/**
* 根导航屏幕 Props 类型
*/
export type RootStackScreenProps<T extends keyof RootStackParamList> =
NativeStackScreenProps<RootStackParamList, T>
/**
* 主 tab 导航屏幕 Props 类型
*/
export type MainTabScreenProps<T extends keyof MainStackParamList> =
CompositeScreenProps<
BottomTabScreenProps<MainStackParamList, T>,
RootStackScreenProps<keyof RootStackParamList>
>
具体使用:
Home.tsx
export default function HomeScreen({
route,
navigation,
}: MainTabScreenProps<"Home">) {
const theme = useTheme()
const [count, setCount] = useState(0)
return (
<View>
<Text
style={{ color: theme.colors.text }}
onPress={() => navigation.jumpTo("Mine", { content: "Hello" })}
>
Hello World: {route.params?.post}
</Text>
<Text>count: {count}</Text>
<Button onPress={() => setCount((c) => c + 1)}>+1</Button>
<Button pressOpacity={0} screen="Login">
Navigate to Login
</Button>
<Button onPress={() => navigation.navigate("Component")}>
Component列表
</Button>
</View>
)
}
嵌套导航
export default function LoginScreen({
navigation,
}: RootStackScreenProps<"Login">) {
return (
<View>
<Text>LoginScreen</Text>
<Button
title="Back to Home"
onPress={() => {
navigation.popTo("Main", {
screen: "Home",
params: {
post: "Login Done!",
},
})
}}
/>
</View>
)
}
5.3 全局配置
配置了全局参数列表类型,可以在任何地方使用。使用 useNavigation
获取导航对象,使用 useRoute
获取路由对象。
declare global {
namespace ReactNavigation {
/**
* 对根栈参数列表类型进行扩展,可以获得全局提示
*/
interface RootParamList extends RootStackParamList {}
}
}