0

I'm trying to embed tab navigators within a drawer navigator, but the drawer navigator sometimes stops working. Code: https://github.com/myplaceonline/testreactexpo/tree/drawernestedtabs

To reproduce the problem:

  1. Click the Screen2 tab at the bottom
  2. Open the drawer
  3. Click Home
  4. Open the drawer
  5. Click Screen2
  6. Nothing happens

Is there a better way to do this and keep the drawer and bottom tabs synchronized and avoid the issue where the drawer stops working?

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {
  createAppContainer,
  createBottomTabNavigator,
  createDrawerNavigator,
  createStackNavigator,
  NavigationActions,
  DrawerActions,
} from 'react-navigation';
import { Ionicons } from '@expo/vector-icons';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#ffffff",
    alignItems: "center",
    justifyContent: "center",
  },
});

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: "Home",
  };
  render() {
    return (
      <View style={styles.container}>
        <Text>Home</Text>
      </View>
    );
  }
}

class Screen2Screen extends React.Component {
  static navigationOptions = {
    title: "Screen2",
  };
  render() {
    return (
      <View style={styles.container}>
        <Text>Screen2</Text>
      </View>
    );
  }
}

const AppScreensTabs = {
  Home: HomeScreen,
  Screen2: Screen2Screen,
};

const AppScreensTabOptions = {
  tabBarOptions: {
    showLabel: true,
  },
  defaultNavigationOptions: ({ navigation }) => ({
    tabBarIcon: ({ focused, horizontal, tintColor }) => {
      const { routeName } = navigation.state;
      let iconName;

      // https://expo.github.io/vector-icons/
      if (routeName === "Home") {
        iconName = "md-home";
      } else if (routeName === "Screen2") {
        iconName = "md-beer";
      }

      return <Ionicons name={iconName} size={25} color={tintColor} />;
    },
  }),
};

const AppScreens = {
  TabHome: createBottomTabNavigator(
    AppScreensTabs,
    Object.assign(
      { initialRouteName: "Home" },
      AppScreensTabOptions,
    )
  ),
  TabScreen2: createBottomTabNavigator(
    AppScreensTabs,
    Object.assign(
      { initialRouteName: "Screen2" },
      AppScreensTabOptions,
    )
  ),
};

const AppScreensStackNavigationOptions = {
  defaultNavigationOptions: ({ navigation }) => ({
    headerLeft: <Ionicons name="md-menu" size={25} onPress={ () => navigation.openDrawer() } style={{ marginLeft: 15 }} />
  })
};

const AppDrawer = createAppContainer(createDrawerNavigator({
  DrawerHome: {
    screen: createStackNavigator(
      AppScreens,
      Object.assign(
        { initialRouteName: "TabHome" },
        AppScreensStackNavigationOptions,
      )
    ),
    navigationOptions: {
      drawerLabel: "Home",
    }
  },
  DrawerScreen2: {
    screen: createStackNavigator(
      AppScreens,
      Object.assign(
        { initialRouteName: "TabScreen2" },
        AppScreensStackNavigationOptions,
      )
    ),
    navigationOptions: {
      drawerLabel: "Screen2",
    }
  },
}));

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

<div data-snack-id="@git/github.com/myplaceonline/testreactexpo@drawernestedtabs" data-snack-platform="ios" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#fafafa;border:1px solid rgba(0,0,0,.08);border-radius:4px;height:505px;width:100%"></div>
<script async src="https://snack.expo.io/embed.js"></script>
Kevin
  • 266
  • 3
  • 16
  • 1
    The drawer seems to work if the active tab in each drawer item is distinct, e.g. when "Home drawer" option is on "Home Tab" and "Screen2 drawer" is on "Screen2 Tab". Have you tried creating a new route object for the Screen2 drawer called `AppScreensTabs2` and used that inside the `TabScreen2` call of `createBottomTabNavigator`? Looks like if referencing the same object in both—`TabHome` and `TabScreen2`—might be the issue. – Marcel Kalveram May 23 '19 at 07:43
  • @MarcelKalveram If I understand you correctly, I duplicated `AppScreensTabs` as `AppScreensTabs2` and then used `AppScreensTabs2` in the `createBottomTabNavigator` call for `TabScreen2`, but the behavior is the same. I guess my more general question is whether there's a better way to do what I'm trying to do? – Kevin May 23 '19 at 19:20
  • Also, maybe it's too hard to duplicate navigation in both a drawer and a tab bar and maybe I'll try without this duplication. – Kevin May 23 '19 at 19:21
  • 1
    @MarcelKalveram Feel free to add an answer like the answer I just posted and I'll mark it as the answer, if you'd like. Cheers. – Kevin May 23 '19 at 21:43
  • I guess that from a UX perspective, it might make more sense to treat them as separate navigation containers instead. Keeping them in sync is not a pattern I remember seeing and think it might be confusing for some people. – Marcel Kalveram May 24 '19 at 07:57

1 Answers1

1

It seems you're trying to keep drawer state and tab state in sync, but I guess that from a UX perspective, it might make more sense to treat them as separate navigation containers, each with their own navigation hierarchy. Keeping them in sync is not straight-forward using react-navigation and is not a pattern I think people will be familiar with when navigation through your app.

Marcel Kalveram
  • 1,295
  • 1
  • 14
  • 22