Skip to content

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-context
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-context

所有导航器必须包裹在 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')} />
}