【React Native】React Navigation 5
前言
在App中,底部TabBar
导航和顶部的导航栏导航是最常见的页面导航方式,而React Native官方推荐的第三方库是@react-navigation
,正好今年React Navigation
发布了5.0版本,与前面的版本差别还挺大,不过使用上更方便了。本文主要讲解React Navigation
5.0及以上版本的使用。
安装
安装核心包
# NPM
npm install @react-navigation/native
# Yarn
yarn add @react-navigation/native
安装依赖
# NPM
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
#Yarn
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
React Native 0.60及以上版本是自动链接库的,不需要手动运行react-native link
,但如果你用React Native开发的是iOS,还需要手动安装pods
来完成库的链接:
npx pod-install ios
把下面这行代码放在入口文件的顶部,比如index.js
或者App.js
import 'react-native-gesture-handler';
最后需要用NavigationContainer
将整个App包裹起来,这个代码一般也是放在入口文件,类似下面这样:
import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
export default function App() {
return (
<NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
);
}
注意: 如果你同时也使用了Redux框架,需要把Provider
放在最外层,将NavigationContainer
包裹在次外层,类似下面这样:
export default class App() {
return (
<Provider store={store}>
<NavigationContainer>
{/* Screen configuration */}
</NavigationContainer>
</Provider>
);
}
使用
React Navigation有多种导航方式:Stack Navigation
、Tab Navigation
和Drawer Navigation
。这里主要讲Stack Navigation
和Tab Navigation
。
顶部导航栏导航
首先需要安装相应的库:
#NPM
npm install @react-navigation/stack
#Yarn
yarn add @react-navigation/stack
Stack Navigation
使用上相对来说比较简单,只需要把需要导航的页面组件封装成Stack.Screen
,然后包裹在Stack.Navigator
就可以了,最上面的Stack.Screen
就是默认页面。
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen key="Home" name="Home" component={HomeScreen} />
</Stack.Navigator>
<Stack.Screen key="detail" name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
也可以在Stack.Screen
中设置导航栏的属性,设置导航栏属性options
有两种方式,一种是带route
参数的,一种是直接设置
/* 带route参数的options */
<Stack.Screen
key={name}
name={name}
component={component}
options={({route}) => ({
headerTitle: route.name,
headerStyle: {
backgroundColor: '#01aaff',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
headerShown: true
})}
/>
/* 直接设置options */
<Stack.Screen
key={name}
name={name}
component={component}
options={{
headerTitle: 'name',
headerStyle: {
backgroundColor: '#01aaff',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
headerShown: true
}}
/>
注意: React框架使用的是虚拟DOM
,使用diff
算法刷新页面显示,在Stack.Screen
中,需要添加key
属性,否则会报警告。
在需要跳转到其他页面时只需要以下代码就可以了,后面跟的是参数。
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
</View>
);
}
如果是用class
方式创建的页面,代码会像下面这样:
export default class HomeScreen extends Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => {
this.props.navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
</View>
);
}
}
注意: 导航跳转可以使用navigation.navigate
或者navigation.push
。如果使用navigate
,会查找当前堆栈中是否有名字一样的路由,如果没有才创建新的路由并跳转,而push
则会直接创建一个新的路由并跳转,也就是说可以重复多次跳转同一个页面。
堆栈导航的返回方式如下:
// 返回到上一页面
navigation.goBack()
// 返回到堆栈里第一个页面
navigation.popToPop()
底部导航栏导航
除了顶部导航栏,最常用的就是底部导航栏了,首先也需要安装相应的库:
# NPM
npm install @react-navigation/bottom-tabs
# Yarn
yarn add @react-navigation/bottom-tabs
底部导航栏的使用方法类似,需要将这些页面组件包装成Tab.Screen
,导航则由框架内部完成,不需要手动控制:
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
底部导航栏属性可以按如下方式设置:
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-information-circle'
: 'ios-information-circle-outline';
} else if (route.name === 'Settings') {
iconName = focused ? 'ios-list-box' : 'ios-list';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
顶部堆栈导航栏与底部导航栏嵌套
Stack Navigation
和Tab Navigation
可以相互多层嵌套,比如登录页跳转到带底部导航栏的主页面,需要把登录相关面和Tab.navigator
一同放到同一个堆栈中,但官方不推荐这种方式,嵌套层次太多会导致维护起来特别麻烦。官方推荐使用一个变量控制当前显示的页面,把不相关的模块隔离在不同的堆栈中,类似下面这样:
const commonScreens = {
Help: HelpScreen,
};
const authScreens = {
SignIn: SignInScreen,
SignUp: SignUpScreen,
};
const userScreens = {
Home: HomeScreen,
Profile: ProfileScreen,
};
<Stack.Navigator>
{Object.entries({
...commonScreens,
...(isLoggedIn ? userScreens : authScreens),
}).map(([name, component]) => (
<Stack.Screen name={name} component={component} />
))}
</Stack.Navigator>;
像isLoggedIn
这样的全局变量动态刷新页面使用Redux架构实现起来会简单一些,具体实现可以参考这篇教程。