react native使用react-navigation时动态显示/隐藏底部导航栏

react native使用react-navigation时动态显示/隐藏底部导航栏

笔者使用react native编写移动端应用程序时发现了一个问题:

  • 使用react-navigation时,当bottomTabNavigation嵌套StackNavigation时,当点击StackNavigation管理的子页面仍然会出现底部导航栏

研究了很久怎么解决这个问题,综合了很多资料终于找到解决方法:
(后续react-navigation进行更新可能导致方法失效,但万变不离其宗)

  1. 找到项目下react-navigation在node module下的目录,打开BottomTabView对应的文件:node_modules@react-navigation\bottom-tabs\src\views\BottomTabView.tsx。这个文件对应的类就是底部导航栏组件
  2. 更改组件的state,往state中加入hidden这一属性(因为是ts,所以需要提前声明是boolean类型)
    react native使用react-navigation时动态显示/隐藏底部导航栏_第1张图片
    react native使用react-navigation时动态显示/隐藏底部导航栏_第2张图片
  3. 在render函数中可以发现原本渲染底部导航栏的代码是{this.renderTabBar()},原本并没有判断是否为子页面的功能。所以在类中现在加上一个check函数,然后在render函数中创建一个tabBar实例来代替之前的this.renderTabBar(),进行判断,如果hidden等于true,则tabBar为一个空组件,如果hidden等于false,则tabBar为一个正常的底部导航栏,从而实现隐藏底部导航栏的效果
  check(){
     
    let count = 0
    for(let i of this.props.state.routes){
     
      console.log('loop')
      if(i.state!=undefined&&i.state.index>0){
     
        count++
      }
    }
    if(count>0){
     
      this.state.hidden=true
    }
    else{
     
      this.state.hidden=false
    }
  }
  render() {
     
    const {
      state, descriptors, navigation, lazy } = this.props;
    const {
      routes } = state;
    const {
      loaded } = this.state;
    /*-----------------改动部分---------------*/
    let tabBar = this.renderTabBar()
    this.check()
    if (this.state.hidden==true) {
     
      tabBar = <View></View>
    }
    /*-------------------------------------*/
    return (
      <NavigationHelpersContext.Provider value={
     navigation}>
        <SafeAreaProviderCompat>
          <View style={
     styles.container}>
            <ScreenContainer style={
     styles.pages}>
              {
     routes.map((route, index) => {
     
                const descriptor = descriptors[route.key];
                const {
      unmountOnBlur } = descriptor.options;
                const isFocused = state.index === index;

                if (unmountOnBlur && !isFocused) {
     
                  return null;
                }

                if (lazy && !loaded.includes(route.key) && !isFocused) {
     
                  // Don't render a screen if we've never navigated to it
                  return null;
                }

                return (
                  <ResourceSavingScene
                    key={
     route.key}
                    style={
     StyleSheet.absoluteFill}
                    isVisible={
     isFocused}
                  >
                    <SceneContent isFocused={
     isFocused}>
                      {
     descriptor.render()}
                    </SceneContent>
                    {
     /*{this.renderTabBar()}*/}
                  </ResourceSavingScene>
                );
              })}
            </ScreenContainer>
            {
     /*{this.renderTabBar()}*/}
            {
     /*!!!!!!!!!!!!!*/}
            {
     tabBar}
          </View>
        </SafeAreaProviderCompat>
      </NavigationHelpersContext.Provider>
    );
  }
}

此处放上整个文件的完整代码供参考

import * as React from 'react';
import {
      View, StyleSheet } from 'react-native';

import {
     
  NavigationHelpersContext,
  TabNavigationState,
  useTheme,
} from '@react-navigation/native';
import {
      ScreenContainer } from 'react-native-screens';

import SafeAreaProviderCompat from './SafeAreaProviderCompat';
import ResourceSavingScene from './ResourceSavingScene';
import BottomTabBar from './BottomTabBar';
import type {
     
  BottomTabNavigationConfig,
  BottomTabDescriptorMap,
  BottomTabNavigationHelpers,
  BottomTabBarProps,
} from '../types';

type Props = BottomTabNavigationConfig & {
     
  state: TabNavigationState;
  navigation: BottomTabNavigationHelpers;
  descriptors: BottomTabDescriptorMap;
};

type State = {
     
  hidden: boolean;
  loaded: string[];
};

function SceneContent({
     
  isFocused,
  children,
}: {
     
  isFocused: boolean;
  children: React.ReactNode;
}) {
     
  const {
      colors } = useTheme();

  return (
    <View
      accessibilityElementsHidden={
     !isFocused}
      importantForAccessibility={
     isFocused ? 'auto' : 'no-hide-descendants'}
      style={
     [styles.content, {
      backgroundColor: colors.background }]}
    >
      {
     children}
    </View>
  );
}

export default class BottomTabView extends React.Component<Props, State> {
     
  static defaultProps = {
     
    lazy: true,
  };

  static getDerivedStateFromProps(nextProps: Props, prevState: State) {
     
    const focusedRouteKey = nextProps.state.routes[nextProps.state.index].key;

    return {
     
      // Set the current tab to be loaded if it was not loaded before
      loaded: prevState.loaded.includes(focusedRouteKey)
        ? prevState.loaded
        : [...prevState.loaded, focusedRouteKey],
    };
  }

  state: State = {
     
    loaded: [this.props.state.routes[this.props.state.index].key],
    hidden: false
  };

  private renderTabBar = () => {
     
    const {
     
      tabBar = (props: BottomTabBarProps) => <BottomTabBar {
     ...props} />,
      tabBarOptions,
      state,
      navigation,
      descriptors,
    } = this.props;
    return tabBar({
     
      ...tabBarOptions,
      state: state,
      descriptors: descriptors,
      navigation: navigation,
    });
  };

  check(){
     
    let count = 0
    for(let i of this.props.state.routes){
     
      console.log('loop')
      if(i.state!=undefined&&i.state.index>0){
     
        count++
      }
    }
    if(count>0){
     
      this.state.hidden=true
    }
    else{
     
      this.state.hidden=false
    }
  }
  render() {
     
    const {
      state, descriptors, navigation, lazy } = this.props;
    const {
      routes } = state;
    const {
      loaded } = this.state;
    let tabBar = this.renderTabBar()
    this.check()
    if (this.state.hidden==true) {
     
      tabBar = <View></View>
    }
    return (
      <NavigationHelpersContext.Provider value={
     navigation}>
        <SafeAreaProviderCompat>
          <View style={
     styles.container}>
            <ScreenContainer style={
     styles.pages}>
              {
     routes.map((route, index) => {
     
                const descriptor = descriptors[route.key];
                const {
      unmountOnBlur } = descriptor.options;
                const isFocused = state.index === index;

                if (unmountOnBlur && !isFocused) {
     
                  return null;
                }

                if (lazy && !loaded.includes(route.key) && !isFocused) {
     
                  // Don't render a screen if we've never navigated to it
                  return null;
                }

                return (
                  <ResourceSavingScene
                    key={
     route.key}
                    style={
     StyleSheet.absoluteFill}
                    isVisible={
     isFocused}
                  >
                    <SceneContent isFocused={
     isFocused}>
                      {
     descriptor.render()}
                    </SceneContent>
                    {
     /*{this.renderTabBar()}*/}
                  </ResourceSavingScene>
                );
              })}
            </ScreenContainer>
            {
     /*{this.renderTabBar()}*/}
            {
     tabBar}
          </View>
        </SafeAreaProviderCompat>
      </NavigationHelpersContext.Provider>
    );
  }
}

const styles = StyleSheet.create({
     
  container: {
     
    flex: 1,
    overflow: 'hidden',
  },
  pages: {
     
    flex: 1,
  },
  content: {
     
    flex: 1,
  },
});

你可能感兴趣的:(前端学习,react,native,js)