自学React-native (第七天)-- 导航嵌套
1. 前言
这里介绍遇到的一些问题。
2.用什么方式来嵌套?
在项目中嵌套navigtor(导航器)是很普遍的情况,这个时候如何处理各个页面间的跳转将是个很令人头疼的问题,接下来我会给出几种处理方案。
- createAppContainer包裹:
这是个比较简单的方式,对子导航器也使用 “createAppContainer” 包裹,这样是可以达成路由嵌套的目的的:
- 父导航器:
import { createStackNavigator, createSwitchNavigator ,createAppContainer} from "react-navigation";
import WelcomePage from "../page/WelcomePage";
import DetailPage from "../page/DetailPage";
import HomePage from "../page/HomePage";
const InitNavigator = createStackNavigator({
WelcomePage: {
screen: WelcomePage,
navigationOptions: {
header: null
}
}
});
const MainNavigator = createStackNavigator({
HomePage: {
screen: HomePage,
navigationOptions: {
title: "HomePage",
header: null
}
},
DetailPage: {
screen: DetailPage,
navigationOptions: {
title: "DetailPage",
// header: null
}
}
});
export default createAppContainer(createSwitchNavigator({
Init: InitNavigator,
Main: MainNavigator
}, {
navigationOptions: {
header: null
}
}
));
请注意最外层是:
export default createAppContainer(createSwitchNavigator({
Init: InitNavigator,
Main: MainNavigator
}, {
navigationOptions: {
header: null
}
}
));
在router Main这里放置的是 “StackNavigator”,需要注意的是如果只是导航器嵌套的话直接将导航器据柄赋给目标路由就可以。注意screen:HomePage 这段就是例子。
const MainNavigator = createStackNavigator({
HomePage: {
screen: HomePage,
navigationOptions: {
title: "HomePage",
header: null
}
},
DetailPage: {
screen: DetailPage,
navigationOptions: {
title: "DetailPage",
// header: null
}
}
});
但是在HomePage.js中还有一个路由,而我们又想在这个路由中获取navigation等一些操作的时候(也就是类似代理操作),我们可能需要获取传入子导航控件中的props属性,这个时候我们就需要做一下处理:
- 子导航器
import React, { Component } from "react";
import { View, Text, StyleSheet } from "react-native";
import { createBottomTabNavigator, createAppContainer } from "react-navigation";
import NavigationUtils from "../navigator/NavigationUtils";
// import DynamicTabNavigator from "../navigator/DynamicTabNavigator";
import MaterialIcons from "react-native-vector-icons/MaterialIcons";
import Ionicons from "react-native-vector-icons/Ionicons";
import Entypo from "react-native-vector-icons/Entypo";
import PopularPage from "../page/Popular";
import TrendingPage from "../page/Trending";
import Favorite from "../page/Favorite";
import MyPage from "../page/MyPage";
const TABS = {
PopularPage: {
screen: PopularPage,
navigationOptions: {
tabBarLabel: "最热",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <MaterialIcons
name={"whatshot"}
size={26}
style={{ color: tintColor }}
></MaterialIcons>
}
}
},
TrendingPage: {
screen: TrendingPage,
navigationOptions: {
tabBarLabel: "趋势",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <Ionicons
name={"md-trending-up"}
size={26}
style={{ color: tintColor }}
></Ionicons>
}
}
},
Favorite: {
screen: Favorite,
navigationOptions: {
tabBarLabel: "收藏",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <MaterialIcons
name={"favorite"}
size={26}
style={{ color: tintColor }}
></MaterialIcons>
}
}
},
MyPage: {
screen: MyPage,
navigationOptions: {
tabBarLabel: "我的",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <Entypo
name={"user"}
size={26}
style={{ color: tintColor }}
></Entypo>
}
}
}
};
class HomePage extends Component{
constructor(props){
super(props);
//此处我将navigation做了个全局备份,为了以后的全局跳转做准备
NavigationUtils.navigation = props.navigation;
}
render(){
const { PopularPage, TrendingPage, Favorite, MyPage } = TABS;
const tabs = {PopularPage, TrendingPage, Favorite,MyPage};//根据需要定制显示的tab
const BottomTab = createAppContainer(createBottomTabNavigator(tabs));
return <BottomTab/>;
}
}
export default HomePage;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
justifyContent: "center",
alignItems: "center"
}
});
注意:createAppContainer(createBottomTabNavigator(tabs));这里将导航器包裹一层createAppContainer然后返回一个Element即可,从上面代码可以看出我将外部的navigation做了个全局备份,如果使用一般的导航器嵌套是没法达成这个目的的。
render(){
const { PopularPage, TrendingPage, Favorite, MyPage } = TABS;
const tabs = {PopularPage, TrendingPage, Favorite,MyPage};//根据需要定制显示的tab
const BottomTab = createAppContainer(createBottomTabNavigator(tabs));
return <BottomTab/>;
}
但是这个做法仍然是有问题的,react-navigation会有一个warning:
YellowBox.js:67 You should only render one navigator explicitly in your app, and other navigators should be rendered by including them in that navigator. Full details at: https://reactnavigation.org/docs/common-mistakes.html#explicitly-rendering-more-than-one-navigator
看过提示信息可以知道,app应该只能渲染一个navigator,其他的navigator应该在这个主navigator中渲染,这里有两个处理方式:
- 关闭warning信息,
console.disableYellowBox = true // 关闭全部黄色警告
- 更换渲染方式,在官方文档中有另外的处理方式。
- 官方建议方式:
查询 https://reactnavigation.org/docs/common-mistakes.html#explicitly-rendering-more-than-one-navigator 后,换种方式进行处理:
import React, { Component } from "react";
import { View, Text, StyleSheet } from "react-native";
import NavigationUtils from "../navigator/NavigationUtils";
import DynamicTabNavigator from "../navigator/DynamicTabNavigator";
class HomePage extends Component{
static router = DynamicTabNavigator.router;
constructor(props){
super(props);
NavigationUtils.navigation = props.navigation;
}
render(){
// DynamicTabNavigator是对导航器的封装
return <DynamicTabNavigator navigation={this.props.navigation} />
}
}
export default HomePage;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
justifyContent: "center",
alignItems: "center"
}
});
DynamicTabNavigator.js
//动态路由
import React, { Component } from "react";
import { createBottomTabNavigator , createAppContainer} from "react-navigation";
import NavigationUtils from "./NavigationUtils";
import MaterialIcons from "react-native-vector-icons/MaterialIcons";
import Ionicons from "react-native-vector-icons/Ionicons";
import Entypo from "react-native-vector-icons/Entypo";
import PopularPage from "../page/Popular";
import TrendingPage from "../page/Trending";
import Favorite from "../page/Favorite";
import MyPage from "../page/MyPage";
import { BottomTabBar } from "react-navigation-tabs";
const TABS = {
PopularPage: {
screen: PopularPage,
navigationOptions: {
tabBarLabel: "最热",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <MaterialIcons
name={"whatshot"}
size={26}
style={{ color: tintColor }}
></MaterialIcons>
}
}
},
TrendingPage: {
screen: TrendingPage,
navigationOptions: {
tabBarLabel: "趋势",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <Ionicons
name={"md-trending-up"}
size={26}
style={{ color: tintColor }}
></Ionicons>
}
}
},
Favorite: {
screen: Favorite,
navigationOptions: {
tabBarLabel: "收藏",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <MaterialIcons
name={"favorite"}
size={26}
style={{ color: tintColor }}
></MaterialIcons>
}
}
},
MyPage: {
screen: MyPage,
navigationOptions: {
tabBarLabel: "我的",
tabBarIcon: ({ tintColor, focused }) => {
//focused 是否被选中
return <Entypo
name={"user"}
size={26}
style={{ color: tintColor }}
></Entypo>
}
}
}
};
//为了自定义风格就需要自定义tabBarComponent
class TabBarComponent extends Component{
constructor(props){
super(props);
this.theme = {
tintColor:props.activeTintColor,
updateTime: Date.now()
}
}
render(){
const {routes,index} = this.props.navigation.state;
if(routes[index].params){
const {theme} = routes[index].params;
//以最新的更新时间为主,防止被其他tab之前的修改覆盖掉
if(theme && theme.updateTime > this.theme.updateTime){
this.theme = theme;
}
}
return <BottomTabBar
{...this.props}
activeTintColor={this.theme.tintColor||this.props.activeTintColor}
/>;
}
}
const AuthenticationNavigator = function() {
const { PopularPage, TrendingPage, Favorite, MyPage } = TABS;
const tabs = {PopularPage, TrendingPage, Favorite,MyPage};//根据需要定制显示的tab
return createBottomTabNavigator(tabs, {
tabBarComponent:TabBarComponent
});
}
export default AuthenticationNavigator();
请注意:
static router = DynamicTabNavigator.router;
<DynamicTabNavigator navigation={this.props.navigation} />
先注入router,再将外部navigation注入子导航器就可以了。
3. 后记
看教程都是使用的createAppContainer包裹方式,但是强迫症不能忍终于去掉了warning提示,现在舒服了。具体怎么抉择大家按照项目实际情况来用哈。