React Native 入门 3 - Navigation
这一段主要讲一些关于 Navigation 的用处,Navigation 有点类似于网页的导航,起到在 app 中切换页面的用途,使用的包为 React Navigation。React Navigation 是一个功能很强大的库,常用的导航功能都有包含,可以搭配 expo 使用 (推荐) 也可以单独与 react native 一起使用。
这里会简单介绍一下 Stack(点击页面中的组件进行跳转)和 Drawer(菜单栏,类似于网页开发的 hamburger)。
Demo 如下:
React Native Demo2
Stack
使用 Stack 之前需要安装一些依赖包:
> npm install @react-navigation/stack
> npx expo install react-native-gesture-handler
基本配置
基本配置方法如下:
-
App.js
import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); export default function App() { return ( <> <NavigationContainer> <Stack.Navigator> <Stack.Screen name="MealsOverview" component={MealsOverviewScreen} options={{}} /> </Stack.Navigator> </NavigationContainer> </> ); }
-
Screen
const MealsOverviewScreen = ({ route, navigation }) => { // implemented jsx here return <View></View>; };
直接挂载在 Stack.Screen
下的组件可以从 props 中获取 navigation
, route
, options
, back
等一些 attributes,这些可以直接在 Stack.Screen
中的 options
中设置,也可以在组件内动态设置。
如果当前组件不是直接挂在于 Stack.Screen
下,class based component 暂时还没有怎么了解怎么配置,FP 可以使用对应的 useHook 进行调用,如:const route = useRoute<ProfileScreenRouteProp>();
。
其中 navigation
可以用于设置与当前组件相关的内容,如设置当前页面的标题,跳转页面等。
route
管理路由相关的部分,如传一些值(类比于 react-router 动态传 id、query param 之类的参数),
页面跳转案例
一个简单的案例为,从首页点击某个子类进去后,子类需要显示对应子类的标题:
那么这里需要完成两个步骤:
-
通过主页进行导航的时候传值到子组件——可以是 title 也可以是 id
const pressHandler = () => { navigation.navigate('MealsOverview', { categoryId: item.id, }); };
注意 ⚠️:
navigation.navigate()
的第一个参数必须要和Stack.Screen
中的name
一致。 -
子组件通过 navigation 进行动态配置
categoryTitle = () => {}; useLayoutEffect(() => { navigation.setOptions({ title: categoryTitle, }); }, [categoryId, navigation]);
这里使用 useLayoutEffect 去尽可能地提升性能。
这样页面中的跳转就基本实现了。
样式配置
每个挂载于 Stack.Screen
的页面都可以通过 headerStyle
和 contentStyle
进行样式的配置,不过这样配置的样式只会在当前页面进行应用,如果想要设置一个 base tone,可以在 Stack.Navigator
中通过 screenOptions
设置:
<Stack.Navigator
screenOptions={{
title: 'All Categories',
headerStyle: { backgroundColor: '#351401' },
headerTintColor: 'white',
contentStyle: { backgroundColor: '#3f2f25' },
}}
></Stack.Navigator>
Drawer
使用 Drawer 同样也需要下载一些额外的依赖包:
npm install @react-navigation/drawer
npx expo install react-native-gesture-handler react-native-reanimated
另外我也遇到了一点运行上的问题,解决方案参考: React Native error: Export namespace should be first transformed by `@babel/plugin-proposal-export-namespace-from,步骤为:
-
安装
npm install react-native-reanimated
-
更新 babel.config.js:
module.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], // add this plugins: ['react-native-reanimated/plugin'], }; };
-
重新运行项目并清理 cache:
npm start --clear
如果单独使用 Drawer,那么基本配置和 Stack 没有什么区别。不过这个案例会使用 Stack 嵌套 Drawer 的用法,因此会稍作一些修改:
-
app.js
import { StatusBar } from 'expo-status-bar'; import { StyleSheet } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createDrawerNavigator } from '@react-navigation/drawer'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { Ionicons } from '@expo/vector-icons'; const Stack = createNativeStackNavigator(); const Drawer = createDrawerNavigator(); const DrawerNavigator = () => { return ( <Drawer.Navigator useLegacyImplementation={true}> <Drawer.Screen name="Categories" component={CategoriesScreen} options={{ title: 'All Categories', drawerIcon: ({ color, size }) => ( <Ionicons color={color} size={size} name="list" /> ), }} /> </Drawer.Navigator> ); }; export default function App() { return ( <> <StatusBar style="dark" /> <NavigationContainer> <Stack.Navigator> <Stack.Screen name="DrawerScreens" component={DrawerNavigator} options={{ headerShown: false, }} /> <Stack.Screen name="MealsOverview" component={MealsOverviewScreen} /> <Stack.Screen name="MealDetails" component={MealDetailScreen} options={{ title: 'About The Meal', }} /> </Stack.Navigator> </NavigationContainer> </> ); }
Drawer 的配置和 Stack 基本一致,这里主要的变化就是将 Stack 的 header 进行隐藏,否则就会出现两个 header。
另一个变化是
useLegacyImplementation={true}
,这个可能跟版本有关,目前我使用的版本"@react-navigation/drawer": "^6.6.2"
,如果不添加这个选项则会报错。