ReactNative 导航
React Navigation
React Navigation 是 React Native 官方推荐的导航方案(非 React Router)。
安装
bash
npm install @react-navigation/native
npm install @react-navigation/native-stack
npm install @react-navigation/bottom-tabs
npm install @react-navigation/drawer
# 依赖
npm install react-native-screens react-native-safe-area-contextnpm install @react-navigation/native
npm install @react-navigation/native-stack
npm install @react-navigation/bottom-tabs
npm install @react-navigation/drawer
# 依赖
npm install react-native-screens react-native-safe-area-contextNavigationContainer
所有导航器必须包裹在 NavigationContainer 中:
tsx
import { NavigationContainer } from '@react-navigation/native'
export default function App() {
return (
<NavigationContainer>
{/* 导航器 */}
</NavigationContainer>
)
}import { NavigationContainer } from '@react-navigation/native'
export default function App() {
return (
<NavigationContainer>
{/* 导航器 */}
</NavigationContainer>
)
}Stack Navigator
堆栈导航,页面间跳转(类似浏览器的 push/pop):
tsx
import { createNativeStackNavigator } from '@react-navigation/native-stack'
const Stack = createNativeStackNavigator()
function HomeScreen({ navigation }) {
return (
<View>
<Button
title="跳转详情"
onPress={() => navigation.navigate('Detail', { id: 123 })}
/>
</View>
)
}
function DetailScreen({ route, navigation }) {
const { id } = route.params // 接收参数
return (
<View>
<Text>Detail {id}</Text>
<Button title="返回" onPress={() => navigation.goBack()} />
</View>
)
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}import { createNativeStackNavigator } from '@react-navigation/native-stack'
const Stack = createNativeStackNavigator()
function HomeScreen({ navigation }) {
return (
<View>
<Button
title="跳转详情"
onPress={() => navigation.navigate('Detail', { id: 123 })}
/>
</View>
)
}
function DetailScreen({ route, navigation }) {
const { id } = route.params // 接收参数
return (
<View>
<Text>Detail {id}</Text>
<Button title="返回" onPress={() => navigation.goBack()} />
</View>
)
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}导航方法
tsx
navigation.navigate('Screen', { params }) // 跳转
navigation.push('Screen', { params }) // 压入新页面(同名也压栈)
navigation.goBack() // 返回
navigation.popToTop() // 返回栈顶
navigation.replace('Screen', { params }) // 替换当前页面
navigation.setParams({ id: 456 }) // 更新参数navigation.navigate('Screen', { params }) // 跳转
navigation.push('Screen', { params }) // 压入新页面(同名也压栈)
navigation.goBack() // 返回
navigation.popToTop() // 返回栈顶
navigation.replace('Screen', { params }) // 替换当前页面
navigation.setParams({ id: 456 }) // 更新参数Tab Navigator
底部标签导航:
tsx
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { Ionicons } from '@expo/vector-icons'
const Tab = createBottomTabNavigator()
function MyTabs() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
const icon = route.name === 'Home' ? 'home' : 'settings'
return <Ionicons name={icon} size={size} color={color} />
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
)
}import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { Ionicons } from '@expo/vector-icons'
const Tab = createBottomTabNavigator()
function MyTabs() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
const icon = route.name === 'Home' ? 'home' : 'settings'
return <Ionicons name={icon} size={size} color={color} />
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
)
}Drawer Navigator
抽屉导航:
tsx
import { createDrawerNavigator } from '@react-navigation/drawer'
const Drawer = createDrawerNavigator()
function MyDrawer() {
return (
<Drawer.Navigator
screenOptions={{
drawerActiveTintColor: '#e91e63',
drawerLabelStyle: { fontSize: 16 },
}}
>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Profile" component={ProfileScreen} />
</Drawer.Navigator>
)
}
// 打开/关闭抽屉
navigation.openDrawer()
navigation.closeDrawer()
navigation.toggleDrawer()import { createDrawerNavigator } from '@react-navigation/drawer'
const Drawer = createDrawerNavigator()
function MyDrawer() {
return (
<Drawer.Navigator
screenOptions={{
drawerActiveTintColor: '#e91e63',
drawerLabelStyle: { fontSize: 16 },
}}
>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Profile" component={ProfileScreen} />
</Drawer.Navigator>
)
}
// 打开/关闭抽屉
navigation.openDrawer()
navigation.closeDrawer()
navigation.toggleDrawer()嵌套导航
Stack 嵌套 Tab 是常见模式:
tsx
function HomeStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="HomeTab" component={HomeStack} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
)
}function HomeStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="HomeTab" component={HomeStack} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
)
}参数传递
传递参数
tsx
navigation.navigate('Detail', {
id: 123,
title: '详情标题',
})navigation.navigate('Detail', {
id: 123,
title: '详情标题',
})接收参数
tsx
function DetailScreen({ route }) {
const { id, title } = route.params
// 默认值
const { id = 0, title = '默认' } = route.params
}function DetailScreen({ route }) {
const { id, title } = route.params
// 默认值
const { id = 0, title = '默认' } = route.params
}配置初始参数
tsx
<Stack.Screen
name="Detail"
component={DetailScreen}
initialParams={{ id: 0 }}
/><Stack.Screen
name="Detail"
component={DetailScreen}
initialParams={{ id: 0 }}
/>导航选项配置
动态配置 header
tsx
function DetailScreen({ navigation, route }) {
const { title } = route.params
React.useLayoutEffect(() => {
navigation.setOptions({
title,
headerRight: () => (
<Button title="编辑" onPress={() => {}} />
),
})
}, [navigation, title])
return <Text>{title}</Text>
}function DetailScreen({ navigation, route }) {
const { title } = route.params
React.useLayoutEffect(() => {
navigation.setOptions({
title,
headerRight: () => (
<Button title="编辑" onPress={() => {}} />
),
})
}, [navigation, title])
return <Text>{title}</Text>
}全局屏幕选项
tsx
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: '#6200ee' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' },
animation: 'slide_from_right', // 转场动画
}}
><Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: '#6200ee' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' },
animation: 'slide_from_right', // 转场动画
}}
>useNavigation
在不接收 navigation prop 的组件中使用:
tsx
import { useNavigation } from '@react-navigation/native'
function MyButton() {
const navigation = useNavigation()
return <Button title="跳转" onPress={() => navigation.navigate('Detail')} />
}import { useNavigation } from '@react-navigation/native'
function MyButton() {
const navigation = useNavigation()
return <Button title="跳转" onPress={() => navigation.navigate('Detail')} />
}
Cao Note