react-navigation4.x动态路由和隐藏底部导航实现

1. 背景

由于rn移动端有一个需求要求通过工作台进入运维中心需要在底部导航栏上展示满足权限过滤的前四个模块。首先我需要把所有路由枚举出来,然后要通过权限过滤得到前四个路由模块,然后在底部导航栏上动态展示出来。那么要怎么在react-navigation4中的createBottomTabNavigator过滤不需要的路由呢?如何在不同页面隐藏底部路由?底部导航如何获取到权限?

2. 如何实现动态路由

由于createBottomTabNavigator API中有一个tabBarComponent属性可以自定义导航组件,而自定义导航组件可以用通过引入
react-navigation-tabs自带的底部自定义组件BottomTabBar,这个组件作用是实现跳转功能和底部导航按钮的样式展示。那么就可以将BottomTabBar封装为自己的组件,实现权限过滤及隐藏的逻辑,从而实现动态路由。

3. 如何隐藏动态路由导航栏

正常情况下,都会用createStackNavigator包裹createBottomTabNavigator。利用tabBarVisible实现:

const AppRouter = createStackNavigator({
  MyTab: MyTab,
  BottomNavigator: BottomNavigator,
});

BottomNavigator.navigationOptions = ({ navigation }) => {
  let tabBarVisible = true;
  if (navigation.state.index > 0) {
    tabBarVisible = false;
  }
  return {
    tabBarVisible,
  };
};

自定义底部导航栏可以直接将BottomTabBar样式设置为display:flase实现:

<BottomTabBar navigation={myNavigation} style={{ display: this.state.isHidden ? 'none' : 'flex' }} />;

可以利用DeviceEventEmitter监听组件的改变,控制底部导航栏的隐藏显示。

4. 如何获取权限数据

可以通过给路由组件设置screenProps属性来给路由组件传递数据。路由组件中可以通过this.props.screenProps获取到传递过来的数据,然后进行路由的权限过滤。

screenProps:后代路由都可以通过this.props.screenProps获取到值。

   <MyTabRouter screenProps={{ module: this.props.user.menuByUserModule }} />;

5. 动画效果及代码

在这里插入图片描述

//router.js
import OperationIndex from './OperationIndex.js';
const RootNavigator = createStackNavigator({
  OperationIndex: {
    screen: OperationIndex,
    navigationOptions: {
      headerShown: false, // 自带头部置空
      gestureEnabled: false // 左侧滑动返回   ios默认开启,android默认关闭
    }
  }
})

//OperationIndex.js
import React from 'react';
import { Image, Text, StyleSheet, StatusBar } from 'react-native';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { BottomTabBar } from 'react-navigation-tabs';
// import { DeviceEventEmitter } from 'react-native';
// 引入页面容器
import OperationCenter from './OperationCenter'
import AssetOperation from './AssetOperation'
import CreateApply from './CreateApply'
import MyApprove from './MyApprove'
import OperationAssist from './OperationAssist'
import WorkorderOperation from './WorkorderOperation'
import SpecialOperation from './SpecialOperation'
import CommandRecheck from './CommandRecheck'
import PasswordTogether from './PasswordTogether'
import ApproveHistory from './ApproveHistory'
import OperationLog from './OperationLog'
import AgentSet from './AgentSet'


import { connect } from "react-redux";
import { bindActionCreators } from 'redux';
import UserRoleProActions from '../../store/actions/user';


const operationInfo = {
  '运维中心': { route: 'OperationCenter', on: require('../../images/Icon/operations/OperationCenter_on.png'), off: require('../../images/Icon/operations/OperationCenter.png') },
  '资产运维': { route: 'AssetOperation', on: require('../../images/Icon/operations/AssetOperation_on.png'), off: require('../../images/Icon/operations/AssetOperation.png') },
  '新建申请': { route: 'CreateApply', on: require('../../images/Icon/operations/CreateApply.png'), off: require('../../images/Icon/operations/CreateApply.png') },
  '待我审批': { route: 'MyApprove', on: require('../../images/Icon/operations/MyApprove_on.png'), off: require('../../images/Icon/operations/MyApprove.png') },
  '运维协助': { route: 'OperationAssist', on: require('../../images/Icon/operations/OperationAssist_on.png'), off: require('../../images/Icon/operations/OperationAssist.png') },

  '工单运维': {
    route: 'WorkorderOperation', on: require('../../images/Icon/operations/WorkorderOperation_on.png'), off: require('../../images/Icon/operations/WorkorderOperation.png')
  },
  '特殊运维': { route: 'SpecialOperation', on: require('../../images/Icon/operations/SpecialOperation_on.png'), off: require('../../images/Icon/operations/SpecialOperation.png') },
  '命令复核': { route: 'CommandRecheck', on: require('../../images/Icon/operations/CommandRecheck_on.png'), off: require('../../images/Icon/operations/CommandRecheck.png') },
  '密码会同': {
    route: 'PasswordTogether', on: require('../../images/Icon/operations/PasswordTogether_on.png'), off: require('../../images/Icon/operations/PasswordTogether.png')
  },
  '审批历史': { route: 'ApproveHistory', on: require('../../images/Icon/operations/ApproveHistory_on.png'), off: require('../../images/Icon/operations/ApproveHistory.png') },
  '运维日志': { route: 'OperationLog', on: require('../../images/Icon/operations/OperationLog_on.png'), off: require('../../images/Icon/operations/OperationLog.png') },
  '代理设置': { route: 'AgentSet', on: require('../../images/Icon/operations/AgentSet_on.png'), off: require('../../images/Icon/operations/AgentSet.png') },
}



/* ****************************** tabbar ****************************** */
const getLabel = (label, tintColor) => {
  return <Text style={[styles.label, { color: tintColor }]}>{label}</Text>;
};
const getBarIcon = (routeName, focused) => {
  let Icon;
  if (routeName === '新建申请') {
    Icon = <Image style={styles.centerAdd} source={operationInfo[routeName].on} />;
  } else {
    Icon = focused ? <Image style={styles.icon} source={operationInfo[routeName].on} />
      :
      <Image style={styles.icon} source={operationInfo[routeName].off} />;
  }
  return Icon;
};
const getOption = (name) => {
  return {
    tabBarLabel: ({ focused, tintColor }) => {
      return getLabel(name, tintColor);
    },
    tabBarIcon: ({ focused, tintColor }) => {
      return getBarIcon(name, focused);
    },
    tabBarOnPress: ({ navigation, defaultHandler }) => {
      StatusBar.setBarStyle('light-content');
      defaultHandler();
    }
  }
}

/* 路由页面 */
let tabbarRoutes = {
  OperationCenter: {
    screen: OperationCenter,
    navigationOptions: getOption('运维中心')
  },
  AssetOperation: {
    screen: AssetOperation,
    navigationOptions: getOption('资产运维')
  },
  CreateApply: {
    screen: CreateApply,
    navigationOptions: getOption('新建申请')
  },
  MyApprove: {
    screen: MyApprove,
    navigationOptions: getOption('待我审批')
  },
  OperationAssist: {
    screen: OperationAssist,
    navigationOptions: getOption('运维协助')
  },
  WorkorderOperation: {
    screen: WorkorderOperation,
    navigationOptions: getOption('工单运维')
  },
  SpecialOperation: {
    screen: SpecialOperation,
    navigationOptions: getOption('特殊运维')
  },
  CommandRecheck: {
    screen: CommandRecheck,
    navigationOptions: getOption('命令复核')
  },
  PasswordTogether: {
    screen: PasswordTogether,
    navigationOptions: getOption('密码会同')
  },
  ApproveHistory: {
    screen: ApproveHistory,
    navigationOptions: getOption('审批历史')
  },
  OperationLog: {
    screen: OperationLog,
    navigationOptions: getOption('运维日志')
  },
  AgentSet: {
    screen: AgentSet,
    navigationOptions: getOption('代理设置')
  }
}

/* routes */
const originalRoutes = [
  { key: 'OperationCenter', routeName: 'OperationCenter', name: '运维中心', params: undefined },
  { key: 'AssetOperation', routeName: 'AssetOperation', name: '资产运维', params: undefined },
  { key: 'CreateApply', routeName: 'CreateApply', name: '新建申请', params: undefined },
  { key: 'MyApprove', routeName: 'MyApprove', name: '待我审批', params: undefined },
  { key: 'OperationAssist', routeName: 'OperationAssist', name: '运维协助', params: undefined },
  { key: 'WorkorderOperation', routeName: 'WorkorderOperation', name: '工单运维', params: undefined },
  { key: 'SpecialOperation', routeName: 'SpecialOperation', name: '特殊运维', params: undefined },
  { key: 'CommandRecheck', routeName: 'CommandRecheck', name: '命令复核', params: undefined },
  { key: 'PasswordTogether', routeName: 'PasswordTogether', name: '密码会同', params: undefined },
  { key: 'ApproveHistory', routeName: 'ApproveHistory', name: '审批历史', params: undefined },
  { key: 'OperationLog', routeName: 'OperationLog', name: '运维日志', params: undefined },
  { key: 'AgentSet', routeName: 'AgentSet', name: '代理设置', params: undefined },
];

//自定义BottomTabBar
class CustomBottomTabBar extends React.Component {
  //这里对navigation进行处理,注意这里不能直接修改props.navigation,会报错,
  //所以只需要传入一个自定义的navigation,而BottomTabBar只会用到navigation.state中routes和index,
  //所以就构造这么一个虚假的navigation就可以了
  state = {
    isHidden: false
  }
  /* 控制底部导航栏显示隐藏 */
  // componentDidMount() {
  //   DeviceEventEmitter.addListener('closeBottomTab', (val) => {
  //     this.setState({ isHidden: val })
  //   });
  //  DeviceEventEmitter.emit('closeBottomTab', false);
  // }
  // componentWillUnmount() {
  //   DeviceEventEmitter.removeListener('closeBottomTab');
  // }

  dealNavigation = () => {
    const { routes, index } = this.props.navigation.state;
    // 根据是否需要显示商品推荐菜单来决定state中的routes
    let finalRoutes = this.filterOperation();
    const currentRoute = routes[index];
    return {
      state: {
        index: finalRoutes.findIndex(route => currentRoute.key === route.key), //修正index
        routes: finalRoutes
      }
    };
  };

  filterOperation = (arr) => {
    if (this.props.screenProps.module) {
      let operation = this.props.screenProps.module.filter(item => item.menuName === '运维中心')[0].childMenu.slice(0, 4);
      let nameArr = operation.map(item => item.menuName);
      return originalRoutes.filter(route => route.name === '运维中心' || nameArr.includes(route.name))
    } else {
      return originalRoutes
    }
  }

  render() {
    const { navigation, ...restProps } = this.props;
    const myNavigation = this.dealNavigation();
    return <BottomTabBar {...restProps} navigation={myNavigation} style={{ display: this.state.isHidden || myNavigation.state.index === -1 ? 'none' : 'flex' }} />;
  }
}

const MyTabRouter = createBottomTabNavigator(tabbarRoutes, {
  tabBarComponent: CustomBottomTabBar,
  backBehavior: 'initialRoute',
  swipeEnabled: true,
  animationEnabled: true,
  initialRouteName: 'OperationCenter',
  tabBarOptions: {
    // label和icon的背景色 活跃状态下(选中) ios
    activeBackgroundColor: 'rgba(0,0,0,0)',
    // label和icon的前景色 活跃状态下(选中)
    activeTintColor: '#FF9023',
    // label和icon的背景色 不活跃状态下(未选中) ios
    inactiveBackgroundColor: 'rgba(0,0,0,0)',
    // label和icon的前景色 不活跃状态下(未选中)
    inactiveTintColor: '#817F7F',
    showIcon: true,
    showLabel: true,
    style: {
      borderTopColor: '#E4E7ED',
      backgroundColor: 'rgba(250,250,250,0.9)',
    }
  }
});

class BottomTab extends React.Component {
  //这里必须有这个静态属性,表示将这个页面视为一个navigator,这样才能和AppStack共用一套导航系统
  static router = MyTabRouter.router;
  render() {
    //screenProps:后代路由都可以通过this.props.screenProps获取到值
    return <MyTabRouter {...this.props} screenProps={{ module: this.props.user.menuByUserModule }} />;
  }
}


const mapStateToProps = state => ({ user: state.user });

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators(UserRoleProActions, dispatch),
  dispatch: dispatch
});
export default connect(mapStateToProps, mapDispatchToProps)(BottomTab);

const styles = StyleSheet.create({
  icon: {

  },
  centerAdd: {
    marginTop: -20
  },
  label: {
    fontSize: 11,
    textAlign: 'center'
  }
});

参考
https://juejin.im/post/5cff4f516fb9a07ea803c0f5

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RN(React Native)和Flutter都是目前流行的跨平台移动应用开发框架。它们都可以用于同时开发iOS和Android应用,具有快速开发、高效性能和良好的用户体验等优点。然而,它们在一些方面有一些不同之处。 首先,RN是由Facebook开发的,它使用JavaScript语言进行开发。它的主要优势是社区庞大,并且有许多成熟的第三方库和组件可供选择。RN还可以直接访问原生代码,因此在需要针对特定平台进行深度集成时更加方便。 相比之下,Flutter是由Google开发的,它使用Dart语言进行开发。与RN相比,Flutter具有更好的性能和更快的渲染速度。它使用自己的渲染引擎Skia直接绘制UI,因此具有更高的性能和平滑的动画效果。此外,Flutter还内置了许多美观的UI组件,可以轻松创建漂亮的用户界面。 然而,RN的优势在于其成熟的生态系统和广泛的社区支持,这意味着在解决问题和获取支持方面更容易。Flutter在这方面还相对较新,并且在解决遇到的问题时可能需要更多的努力。 最后,选择RN还是Flutter,取决于您的具体需求和团队的技术能力。如果您已经熟悉JavaScript或React,并且需要快速开发原生应用,那么RN可能是一个不错的选择。而如果您对性能、动画效果和漂亮的UI设计有更高的要求,而且不介意学习新的编程语言和框架,那么Flutter可能更适合您。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值