【问题标题】:React-Navigation 3: Open modal with createBottomTabNavigator and createStackNavigatorReact-Navigation 3:使用 createBottomTabNavigator 和 createStackNavigator 打开模式
【发布时间】:2019-02-15 09:06:35
【问题描述】:

我知道之前有人问过这个问题,但只针对较旧版本的 react-navigation。从那以后,一些事情发生了变化。 createBottomTabNavigator 可以更快地创建底部导航器,并且函数 jumpToIndex() 不再存在。

我的问题是如何创建一个类似 Instagram 的底部标签,其中第一、第二、第四和第五个导航按钮的作用类似于通常的标签导航器,而中间按钮 (screen3) 打开模式 screen3Modal

我已经在 react-navigation 3.x.x 中尝试过,使用 createBottomTabNavigatorcreateStackNavigator

import React, { Component, } from 'react';
import { createBottomTabNavigator, createStackNavigator, createAppContainer, } from 'react-navigation';
import { Screen1, Screen2, Screen3, Screen4, Screen5 } from './screens';

const TabNavigator = createBottomTabNavigator({
  screen1: { screen: Screen1, },
  screen2: { screen: Screen2, },
  screen3: { 
    screen: () => null, 
    navigationOptions: () => ({
      tabBarOnPress: () => this.props.navigation.navigate('screen3Modal')
    })
  },
  screen4: { screen: Screen4, },
  screen5: { screen: Screen5, },
});

const StackNavigator = createStackNavigator({
  Home: { screen: TabNavigator },
  screen3Modal: { screen: Screen3, },
},
{
  initialRouteName: 'Home',
});

const StackNavigatorContainer = createAppContainer(StackNavigator);

export default class App extends Component {
  render() {
    return <StackNavigatorContainer />;
  }
}

此代码创建选项卡导航和模式导航。模式可以从另一个屏幕打开,但它不能在选项卡导航器中工作。我收到错误消息undefined is not an object (evaluating '_this.props.navigation')

【问题讨论】:

    标签: javascript reactjs react-native react-navigation


    【解决方案1】:

    我找到了一个相对简单的解决方案: 用display:"none"隐藏原来的导航栏

    const TabNavigator = createBottomTabNavigator(
      {
        screen1: Screen1,
        screen2: Screen2,
        screen4: Screen4,
        screen5: Screen5,
      }, {
        tabBarOptions: {
          style: { display: "none", }
        }
      },
    );
    
    const StackNavigator = createStackNavigator(
      {
        Home: TabNavigator,
        screen3: Screen3
      }, {
        mode: 'modal',
      }
    )
    
    export default createAppContainer(StackNavigator);
    

    并在每个屏幕上创建一个新的导航栏

    <View style={{ flexDirection: "row", height: 50, justifyContent: "space-evenly", alignItems: "center", width: "100%" }}>
      <TouchableOpacity onPress={() => this.props.navigation.navigate("screen1")}><Text>1</Text></TouchableOpacity>
      <TouchableOpacity onPress={() => this.props.navigation.navigate("screen2")}><Text>2</Text></TouchableOpacity>
      <TouchableOpacity onPress={() => this.props.navigation.navigate("screen3")}><Text>3</Text></TouchableOpacity>
      <TouchableOpacity onPress={() => this.props.navigation.navigate("screen4")}><Text>4</Text></TouchableOpacity>
      <TouchableOpacity onPress={() => this.props.navigation.navigate("screen5")}><Text>5</Text></TouchableOpacity>
    </View>
    

    【讨论】:

    • 岩石固溶体。但是,您将如何显示活动选项卡?在您的示例中,没有活动选项卡概念。
    • 这不是一个好的解决方案,您应该在整个应用程序中只有一个底部导航组件。将相同的组件添加到所有屏幕会在导航时闪烁,并且看起来和感觉都不再像“标签”。
    【解决方案2】:

    您可以将所有内容包装在同一个 StackNavigator 中,这样您就可以轻松导航到其他路线。这里我将 screen3 作为默认路由传递,但您可以将其更改为您想要的任何内容。

    import React, { Component, } from 'react';
    import { createBottomTabNavigator, createStackNavigator, createAppContainer, } from 'react-navigation';
    import { Screen1, Screen2, Screen3, Screen4, Screen5 } from './screens';
    
    const TabNavigator = createBottomTabNavigator({
      screen1: { screen: Screen1, },
      screen2: { screen: Screen2, },
      screen3: { screen: () => null, }, //this.props.navigation.navigate('screen3Modal')
      screen4: { screen: Screen4, },
      screen5: { screen: Screen5, },
    });
    
    const StackNavigator = createStackNavigator({
      Home: { screen: TabNavigator },
      screen3Modal: { screen: Screen3, },
    },
    {
      initialRouteName: 'screen3Modal',
    });
    
    const StackNavigatorContainer = createAppContainer(StackNavigator);
    
    export default class App extends Component {
      render() {
        return <StackNavigatorContainer />;
      }
    }
    

    【讨论】:

    • 如何导航到模态框?我试过tabBarOnPress: () =&gt; this.props.navigation.navigate('screen3Modal'),但我得到一个错误:undefined is not an object (evaluating '_this.props.navigation')
    • 您尝试从哪个屏幕导航?你有你的constructor(props) {super(props)}设置吗?
    • 我在上面编辑了我的帖子。我希望现在可以更好地理解。初始屏幕应为Home(选项卡导航器),当按下screen3 时,应打开一个模式,覆盖整个页面并隐藏底部导航器。不,我没有设置。我所拥有的一切都在上面的代码中。
    • 啊,好吧,之前不是很清楚,你在这里检查过这个答案吗? stackoverflow.com/questions/54024006/…我相信你正在尝试做的事情不可能像你现在做的那样
    • 我尝试了一个新的解决方案:我创建了一个新屏幕,其唯一目的是在componentWillMount 上打开模式。现在我可以像您发送给我的链接一样使用选项卡导航器打开这个新屏幕,但这会产生一些其他问题。使用tabBarOnPressforceUpdate 这个新屏幕会不会更干净,然后应该在加载时打开模式?如果可能,我应该如何使用forceUpdate 函数?我没有让它工作。
    【解决方案3】:

    嗯,我花了好几个小时来解决这个问题。

    这是完全有效且经过测试的解决方案:

    1. 通过包含选项卡堆栈和要打开的模式导航堆栈来设置您的应用容器:
      const FinalTabsStack = createStackNavigator(
        {
          tabs: TabNavigator,
          screen1: Screen1Navigator,
        }, {
          mode: 'modal',
        }
      )
    
    1. 根据this 指南使用该选项卡堆栈创建应用容器

    2. createBottomTabNavigator 中的 TabNavigator 内部,为特定选项卡 (screen3) 返回 null 组件(通过 react-navigator 关闭导航)并通过创建自定义组件在 defaultNavigationOptions 内手动处理选项卡它。

      const TabNavigator = createBottomTabNavigator({
        screen1: Screen1Navigator,
        screen2: Screen2Navigator,
        screen3: () => null,
        screen4: Screen4Navigator,
        screen5: Screen5Navigator,
    }
        defaultNavigationOptions: ({ navigation }) => ({
          mode: 'modal',
          header: null,
          tabBarIcon: ({ focused }) => {
            const { routeName } = navigation.state;
            if (routeName === 'screen3') {
              return <Screen3Tab isFocused={focused} />;
            }
          },
        }),
    
    1. 使用 TouchableWithoutFeedback 和 onPress 在自定义选项卡组件 Screen3Tab 内手动处理单击。 Screen3Tab 组件内部:
      <TouchableWithoutFeedback onPress={this.onPress}>
        <Your custom tab component here />
      </TouchableWithoutFeedback>
    
    
    1. 一旦你捕捉到 onPress dispatch redux 事件
      onPress = () => {
        this.props.dispatch({ type: 'NAVIGATION_NAVIGATE', payload: {
          key: 'screen3',
          routeName: 'screen3',
        }})
      }
    
    1. 使用Navigation Service 处理调度事件。我正在使用redux-saga
      NavigationService.navigate(action.payload);
    

    有点复杂但有效。

    【讨论】:

      【解决方案4】:

      我根据this github issue 找到了更好的解决方案。 您只需添加特定的选项卡配置,在您的情况下 screen3 事件导航选项(您已经拥有它),您已经非常接近了,但是,您必须将导航作为参数接收,因为没有任何上下文this 在您使用它时。要更正您编写的第一个代码,我会将其更改为此并且它会起作用:

      navigationOptions: ({ navigation }) => ({
        tabBarOnPress: ({ navigation }) => {
          navigation.navigate("screen3Modal");
        }
      })
      

      【讨论】:

        【解决方案5】:

        可以做的是防止标签按下事件的默认操作,并导航到您想要的动画屏幕。

        就我而言,我想在 iOS 中以来自 iOS 13 的演示风格打开一个模式。我所做的是阻止下面的默认操作

           <Tab.Screen name="screen1" 
            component={ EmptyScreen } // empty screen becuase I want nothing to present here so it was a component that returns null which is given bellow   
            listeners={({ navigation, route }) => ({
              tabPress: e => {
                e.preventDefault();
                navigation.navigate('modalscreen');
              },
            })} />
        
        
        const EmptyScreen = () => {
          return null
        }
        
        上面代码中使用的

        'modelscreen' 来自如下所示的导航堆栈。上面代码中 Tab.Screen 的选项卡导航器是 otherstacknavigation 中的屏幕之一,用于下面的导航。

        const MainStack = ({ initialRoute }) => {
          return (
              <Stack.Navigator 
                    initialRouteName="otherstacknavigation" 
                    screenOptions={{
                        headerShown: false,
                        gestureEnabled: true,
                        cardOverlayEnabled: true,
                        ...TransitionPresets.ModalPresentationIOS
                    }}>
                <Stack.Screen name='modalscreen' 
                              component={modalscreen}
                              options={{
                                  headerShown: false
                              }} />
                <Stack.Screen name='otherstacknavigation' 
                              component={otherstacknavigation}
                              initialParams={{ initialRoute: initialRoute }}/>
              </Stack.Navigator>
          );
        }
        
        export default MainStack;
        

        您可以像下面这样导入 TransitionPresets

            import { createStackNavigator, TransitionPresets } from '@react-navigation/stack'
        

        所以,就我而言,我有第一个主堆栈导航器,即上面的 MainStack。它有另一个堆栈导航器,即 MainStack 上方的 otherstacknavigation。那个 otherstacknavigation 有一个屏幕,其组件是 Tab navigator。上面代码中的 Tab.Screen 来自该 Tab Navigator。现在,从这个选项卡导航器中的一个选项卡,我正在导航到 MainStack 中的“modelscreen”。我必须创建这个 MainStack 导航器来实现这些功能,否则我的代码将从 otherstacknavigation 开始。因为,ModalPresentationIOS 只能应用于堆栈级别而不是屏幕级别。

        我正在使用 react-navigation-5.*,我认为它已经回答了在这种情况下可能出现的多个问题。

        【讨论】:

          猜你喜欢
          • 2020-05-27
          • 1970-01-01
          • 2019-01-30
          • 1970-01-01
          • 1970-01-01
          • 2020-01-09
          • 1970-01-01
          • 1970-01-01
          • 2019-09-16
          相关资源
          最近更新 更多