最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React-Navigation 3: Open modal with createBottomTabNavigator and createStackNavigator - Stack Overflow

programmeradmin3浏览0评论

I know this question has been asked before, but only for older versions of react-navigation. Since then a few things have changed. createBottomTabNavigator makes it much faster to create a bottom navigator and the function jumpToIndex() doesn't exist anymore.

My Question is how to create an Instagram-like bottom tab, where the first, second, fourth and fifth navigation buttons act like usual tab navigators and the middle button (screen3) opens the modal screen3Modal.

I have tried it in react-navigation 3.x.x, using createBottomTabNavigator and createStackNavigator.

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 />;
  }
}

This code creates the tab navigation and modal navigation. The modal can be opened from another screen, but it doesn't work from within the tab navigator. I get the errormessage undefined is not an object (evaluating '_this.props.navigation')

I know this question has been asked before, but only for older versions of react-navigation. Since then a few things have changed. createBottomTabNavigator makes it much faster to create a bottom navigator and the function jumpToIndex() doesn't exist anymore.

My Question is how to create an Instagram-like bottom tab, where the first, second, fourth and fifth navigation buttons act like usual tab navigators and the middle button (screen3) opens the modal screen3Modal.

I have tried it in react-navigation 3.x.x, using createBottomTabNavigator and createStackNavigator.

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 />;
  }
}

This code creates the tab navigation and modal navigation. The modal can be opened from another screen, but it doesn't work from within the tab navigator. I get the errormessage undefined is not an object (evaluating '_this.props.navigation')

Share Improve this question edited Feb 15, 2019 at 10:11 Gabriel Winkler asked Feb 15, 2019 at 9:06 Gabriel WinklerGabriel Winkler 3153 silver badges14 bronze badges
Add a ment  | 

5 Answers 5

Reset to default 5

I have found a relatively easy solution: Hide the original navigation bar with 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);

And create a new navigation bar on each screen

<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>

You can wrap everything inside the same StackNavigator, that way you can navigate to other routes easily. Here i'm passing the screen3 as default route, but you can change that to whatever you'd like.

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 />;
  }
}

Well I have spent hours upon hours to solve this problem.

Here's fully working & tested solution:

  1. Set up your app container by including tabs stack & to be opened modal navigation stack:
  const FinalTabsStack = createStackNavigator(
    {
      tabs: TabNavigator,
      screen1: Screen1Navigator,
    }, {
      mode: 'modal',
    }
  )
  1. Create app container with that tabs stack per this guide

  2. Inside the TabNavigator in the createBottomTabNavigator return null ponent for specific tab (screen3) (to turn off navigation by react-navigator) and handle the tab manually inside the defaultNavigationOptions by creating custom ponent for it.

  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. Handle click manually inside a custom tab ponent Screen3Tab with TouchableWithoutFeedback & onPress. Inside Screen3Tab ponent:
  <TouchableWithoutFeedback onPress={this.onPress}>
    <Your custom tab ponent here />
  </TouchableWithoutFeedback>

  1. Once you catch onPress dispatch redux event
  onPress = () => {
    this.props.dispatch({ type: 'NAVIGATION_NAVIGATE', payload: {
      key: 'screen3',
      routeName: 'screen3',
    }})
  }
  1. Handle dispatched event using Navigation Service. I'm using redux-saga for it
  NavigationService.navigate(action.payload);

A bit plicated but works.

I've found a better solution based on this github issue. You just have to add in the specific tab configuration, in your case screen3 the event navigationOptions(you already had it), you were pretty close, but, you had to receive the navigation as a parameter, because there isn't any context for this as you were using it. To correct the first code that you wrote I would change it to this and it would work:

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

what can be done is to prevent default action of tab press event and navigate to your desired screen with desired animation.

In my case, i wanted to open a modal in iOS in presentation style which came from iOS 13. what I did was prevent the default action like below

   <Tab.Screen name="screen1" 
    ponent={ EmptyScreen } // empty screen becuase I want nothing to present here so it was a ponent that returns null which is given bellow   
    listeners={({ navigation, route }) => ({
      tabPress: e => {
        e.preventDefault();
        navigation.navigate('modalscreen');
      },
    })} />


const EmptyScreen = () => {
  return null
}

'modelscreen' used in above code is from a navigation stack which looks like bellow. Tab navigator for Tab.Screen in the above code is one of the screen in otherstacknavigation which is used in the navigation below.

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

export default MainStack;

you can import TransitionPresets like bellow;

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

So, in my case, i have first main stack navigator which is MainStack above. It has another stack navigator which is otherstacknavigation in above MainStack. That otherstacknavigation has a screen whose ponent is Tab navigator. and Tab.Screen in above code is from that Tab Navigator. Now, from one of the tab from this tab navigator, i am navigating to 'modelscreen' which is in MainStack. I had to create this MainStack navigator just to achieve this features otherwise my code would start from otherstacknavigation. Because, ModalPresentationIOS can only be applied to stack level not screen level.

I am using react-navigation-5.* and i think it has answered multiple questions that can arise in such situation.

发布评论

评论列表(0)

  1. 暂无评论