React Navigation导航进阶 #

概述 #

本章节介绍 React Navigation 的高级功能,包括参数传递、深层链接、导航状态管理和自定义导航器。

参数传递 #

传递复杂参数 #

tsx
import React from 'react';
import {View, Button} from 'react-native';

type Product = {
  id: string;
  name: string;
  price: number;
  description: string;
};

const ProductListScreen = ({navigation}) => {
  const product: Product = {
    id: '1',
    name: 'iPhone 15',
    price: 999,
    description: 'Latest iPhone model',
  };

  return (
    <View>
      <Button
        title="View Details"
        onPress={() =>
          navigation.navigate('ProductDetails', {
            product,
            from: 'ProductList',
          })
        }
      />
    </View>
  );
};

使用 TypeScript 类型 #

tsx
import React from 'react';
import {View, Text} from 'react-native';
import {NativeStackScreenProps} from '@react-navigation/native-stack';

type RootStackParamList = {
  ProductList: undefined;
  ProductDetails: {
    product: {
      id: string;
      name: string;
      price: number;
    };
    from?: string;
  };
};

type Props = NativeStackScreenProps<RootStackParamList, 'ProductDetails'>;

const ProductDetailsScreen: React.FC<Props> = ({route}) => {
  const {product, from} = route.params;

  return (
    <View>
      <Text>{product.name}</Text>
      <Text>Price: ${product.price}</Text>
      {from && <Text>From: {from}</Text>}
    </View>
  );
};

更新参数 #

tsx
import React from 'react';
import {View, Button} from 'react-native';

const DetailsScreen = ({navigation, route}) => {
  return (
    <View>
      <Text>Current count: {route.params.count || 0}</Text>
      <Button
        title="Increase"
        onPress={() =>
          navigation.setParams({
            count: (route.params.count || 0) + 1,
          })
        }
      />
    </View>
  );
};

初始参数 #

tsx
<Stack.Screen
  name="Details"
  component={DetailsScreen}
  initialParams={{count: 0, title: 'Default Title'}}
/>

导航事件 #

监听导航事件 #

tsx
import React from 'react';
import {Text} from 'react-native';
import {useNavigation} from '@react-navigation/native';

const MyScreen = () => {
  const navigation = useNavigation();

  React.useEffect(() => {
    const unsubscribe = navigation.addListener('focus', () => {
      console.log('Screen focused');
    });

    const blurUnsubscribe = navigation.addListener('blur', () => {
      console.log('Screen blurred');
    });

    return () => {
      unsubscribe();
      blurUnsubscribe();
    };
  }, [navigation]);

  return <Text>My Screen</Text>;
};

可用事件 #

事件 说明
focus 页面获得焦点
blur 页面失去焦点
state 导航状态变化
beforeRemove 页面即将被移除

阻止返回 #

tsx
import React, {useEffect} from 'react';
import {BackHandler, Alert} from 'react-native';
import {useNavigation} from '@react-navigation/native';

const FormScreen = () => {
  const navigation = useNavigation();

  useEffect(() => {
    const unsubscribe = navigation.addListener('beforeRemove', e => {
      e.preventDefault();

      Alert.alert(
        '确认离开?',
        '您有未保存的更改,确定要离开吗?',
        [
          {text: '取消', style: 'cancel'},
          {
            text: '离开',
            style: 'destructive',
            onPress: () => navigation.dispatch(e.data.action),
          },
        ],
      );
    });

    return unsubscribe;
  }, [navigation]);

  return <Text>Form Screen</Text>;
};

深层链接 #

配置深层链接 #

tsx
import React from 'react';
import {NavigationContainer, LinkingOptions} from '@react-navigation/native';

const linking: LinkingOptions<RootStackParamList> = {
  prefixes: ['myapp://', 'https://myapp.com'],
  config: {
    screens: {
      Home: '',
      ProductDetails: {
        path: 'product/:id',
        parse: {
          id: (id: string) => id,
        },
      },
      Profile: 'profile/:userId',
    },
  },
};

const App = () => {
  return (
    <NavigationContainer linking={linking}>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="ProductDetails" component={ProductDetailsScreen} />
        <Stack.Screen name="Profile" component={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

处理深层链接 #

tsx
import {Linking} from 'react-native';

const App = () => {
  const [initialUrl, setInitialUrl] = useState<string | null>(null);

  useEffect(() => {
    Linking.getInitialURL().then(url => {
      setInitialUrl(url);
    });

    const subscription = Linking.addEventListener('url', ({url}) => {
      console.log('Deep link received:', url);
    });

    return () => subscription.remove();
  }, []);

  return (
    <NavigationContainer linking={linking}>
      {}
    </NavigationContainer>
  );
};

iOS 配置 #

ios/MyApp/Info.plist 中添加:

xml
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

Android 配置 #

android/app/src/main/AndroidManifest.xml 中添加:

xml
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="myapp" />
</intent-filter>

导航状态管理 #

获取当前状态 #

tsx
import React from 'react';
import {useNavigationState} from '@react-navigation/native';

const CurrentScreen = () => {
  const state = useNavigationState(state => state);
  const currentRoute = state.routes[state.index];

  return <Text>Current route: {currentRoute.name}</Text>;
};

导航 Ref #

tsx
import React, {useRef} from 'react';
import {NavigationContainerRef} from '@react-navigation/native';

const App = () => {
  const navigationRef = useRef<NavigationContainerRef>(null);

  const navigateToDetails = () => {
    navigationRef.current?.navigate('Details', {id: '123'});
  };

  return (
    <>
      <NavigationContainer ref={navigationRef}>
        <Stack.Navigator>
          {}
        </Stack.Navigator>
      </NavigationContainer>
    </>
  );
};

状态持久化 #

tsx
import React, {useState, useEffect} from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {NavigationContainer} from '@react-navigation/native';

const PERSISTENCE_KEY = 'NAVIGATION_STATE';

const App = () => {
  const [isReady, setIsReady] = useState(false);
  const [initialState, setInitialState] = useState();

  useEffect(() => {
    const restoreState = async () => {
      try {
        const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY);
        const state = savedStateString ? JSON.parse(savedStateString) : undefined;
        setInitialState(state);
      } finally {
        setIsReady(true);
      }
    };

    restoreState();
  }, []);

  if (!isReady) {
    return null;
  }

  return (
    <NavigationContainer
      initialState={initialState}
      onStateChange={state => {
        AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state));
      }}>
      {}
    </NavigationContainer>
  );
};

自定义导航器 #

自定义 Tab Bar #

tsx
import React from 'react';
import {View, TouchableOpacity, Text, StyleSheet} from 'react-native';
import {BottomTabBarProps} from '@react-navigation/bottom-tabs';

const CustomTabBar: React.FC<BottomTabBarProps> = ({state, descriptors, navigation}) => {
  return (
    <View style={styles.container}>
      {state.routes.map((route, index) => {
        const {options} = descriptors[route.key];
        const label = options.tabBarLabel ?? route.name;
        const isFocused = state.index === index;

        const onPress = () => {
          const event = navigation.emit({
            type: 'tabPress',
            target: route.key,
            canPreventDefault: true,
          });

          if (!isFocused && !event.defaultPrevented) {
            navigation.navigate(route.name);
          }
        };

        return (
          <TouchableOpacity
            key={route.key}
            accessibilityRole="button"
            accessibilityState={isFocused ? {selected: true} : {}}
            onPress={onPress}
            style={styles.tab}>
            <Text style={[styles.label, isFocused && styles.focusedLabel]}>
              {label}
            </Text>
          </TouchableOpacity>
        );
      })}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    backgroundColor: '#fff',
    borderTopWidth: 1,
    borderTopColor: '#eee',
  },
  tab: {
    flex: 1,
    alignItems: 'center',
    paddingVertical: 12,
  },
  label: {
    fontSize: 12,
    color: '#999',
  },
  focusedLabel: {
    color: '#007AFF',
    fontWeight: 'bold',
  },
});

使用自定义 Tab Bar #

tsx
<Tab.Navigator tabBar={props => <CustomTabBar {...props} />}>
  <Tab.Screen name="Home" component={HomeScreen} />
  <Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>

导航动画 #

自定义过渡动画 #

tsx
import React from 'react';
import {CardStyleInterpolators} from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator
        screenOptions={{
          cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
        }}>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

可用的插值器 #

插值器 效果
forHorizontalIOS iOS 水平滑动
forVerticalIOS iOS 垂直滑动
forModalPresentationIOS iOS 模态展示
forFadeFromBottomAndroid Android 淡入
forRevealFromBottomAndroid Android 从底部揭示

模态窗口 #

配置模态窗口 #

tsx
<Stack.Navigator>
  <Stack.Screen name="Home" component={HomeScreen} />
  <Stack.Screen
    name="Modal"
    component={ModalScreen}
    options={{
      presentation: 'modal',
      headerShown: false,
    }}
  />
</Stack.Navigator>

全屏模态 #

tsx
<Stack.Screen
  name="FullScreenModal"
  component={FullScreenModalScreen}
  options={{
    presentation: 'fullScreenModal',
    headerShown: false,
  }}
/>

导航守卫 #

认证流程 #

tsx
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

const App = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  return (
    <NavigationContainer>
      <Stack.Navigator>
        {isAuthenticated ? (
          <>
            <Stack.Screen name="Home" component={HomeScreen} />
            <Stack.Screen name="Profile" component={ProfileScreen} />
          </>
        ) : (
          <>
            <Stack.Screen name="Login" component={LoginScreen} />
            <Stack.Screen name="Register" component={RegisterScreen} />
          </>
        )}
      </Stack.Navigator>
    </NavigationContainer>
  );
};

条件导航 #

tsx
import React from 'react';
import {View, Button} from 'react-native';
import {useNavigation} from '@react-navigation/native';

const ProtectedButton = () => {
  const navigation = useNavigation();
  const {isAuthenticated} = useAuth();

  const handlePress = () => {
    if (isAuthenticated) {
      navigation.navigate('Profile');
    } else {
      navigation.navigate('Login', {redirect: 'Profile'});
    }
  };

  return <Button title="View Profile" onPress={handlePress} />;
};

总结 #

React Navigation 的高级功能包括:

  • 参数传递:传递复杂对象和类型安全
  • 导航事件:监听页面焦点和状态变化
  • 深层链接:支持 URL Scheme 和 Universal Links
  • 状态管理:获取和持久化导航状态
  • 自定义导航器:自定义 Tab Bar 和过渡动画
  • 导航守卫:实现认证流程

掌握这些高级功能,可以构建更加复杂和灵活的导航系统。

继续学习 状态管理基础,了解 React Native 的状态管理方案。

最后更新:2026-03-28