react-navigation / react-navigation

Routing and navigation for your React Native apps
https://reactnavigation.org
23.48k stars 5.01k forks source link

Stacking Tab Navigators shows blank screens #1627

Closed ghost closed 7 years ago

ghost commented 7 years ago

Current Behavior

Screens' contents are not shown / the screens are blank.

Expected Behavior

Should see the Screen's contents

Your Environment

This is my navigator structure

const MainNavigator = TabNavigator({
            welcome: { screen: WelcomeScreen },
            auth: { screen: AuthScreen },
            main: {
                screen: TabNavigator({
                    grid: { screen: GridScreen },
                    review: {
                        screen: StackNavigator({
                            review: { screen: ReviewScreen },
                            settings: { screen: SettingsScreen }
                        })
                    }
                })
            }
        });

The screen is blank when I navigate to "Main", in there I also cannot switch between the Grid and Review tabs.

software version
react-navigation "^1.0.0-beta.9"
react-native "0.44.0"
keeleycarrigan commented 7 years ago

I experienced this as well to some degree. My setup was a little different. My nested tabs were the first screen in a stack navigator. If I changed the first screen to a normal view everything showed up properly but if the first screen was a tab navigator the view wouldn't show up.

rafaelmotta commented 7 years ago

Same here. Only with Android.

keeleycarrigan commented 7 years ago

So it looks like when I add:

animationEnabled: false,
swipeEnabled: false

to my root navigator the inner tab views show up, but the inner tabs don't swipe like normal (or even when I use the Android style tabs on iOS) and clicking the inner tabs doesn't change the view. Here's an example I made modifying one of Facebook's examples using react-native-material-bottom-navigation & react-native-detect-navbar-android. In iOS it works fine.

import React, { Component } from 'react';
import {
  Button,
  Platform,
  ScrollView,
  StatusBar
} from 'react-native';
import {
  StackNavigator,
  TabNavigator,
} from 'react-navigation';
import { NavigationComponent } from 'react-native-material-bottom-navigation';
import { DetectNavbar } from 'react-native-detect-navbar-android';

import Ionicons from 'react-native-vector-icons/Ionicons';
import SampleText from './sample-txt';

const MyNavScreen = ({ navigation, banner }) => (
  <ScrollView>
    <SampleText>{banner}</SampleText>
    <Button
      onPress={() => navigation.navigate('Profile', { name: 'Jordan' })}
      title="Go to a profile screen"
    />
    <Button
      onPress={() => navigation.navigate('NotifSettings')}
      title="Go to notification settings"
    />
    <Button
      onPress={() => navigation.navigate('SettingsTab')}
      title="Go to settings"
    />
    <Button
      onPress={() => navigation.goBack(null)}
      title="Go back"
    />
  </ScrollView>
);

const MyHomeScreen = ({ navigation }) => (
  <MyNavScreen
    banner="Home Screen"
    navigation={navigation}
  />
);

const MyHomeScreen2 = ({ navigation }) => (
  <MyNavScreen
    banner="Home Screen"
    navigation={navigation}
  />
);

const MyProfileScreen = ({ navigation }) => (
  <MyNavScreen
    banner={`${navigation.state.params.name}s Profile`}
    navigation={navigation}
  />
);
MyProfileScreen.navigationOptions = ({ navigation }) => ({
  title: `${navigation.state.params.name}'s Profile!`,
});

const MyNotificationsSettingsScreen = ({ navigation }) => (
  <MyNavScreen
    banner="Notification Settings"
    navigation={navigation}
  />
);

const MySettingsScreen = ({ navigation }) => (
  <MyNavScreen
    banner="Settings"
    navigation={navigation}
  />
);

const stackConfig = {};

if (Platform.OS === 'android') {
    stackConfig = {
        navigationOptions: {
            headerStyle: {
              paddingTop: StatusBar.currentHeight,
              height: 56 + StatusBar.currentHeight,
              elevation: 0
            }
        }
    };
}

const HomeTabs = {
    HomeTabOne: {
        screen: MyHomeScreen,
    },
    HomeTabTwo: {
        screen: MyHomeScreen2,
    }
};

const mainTabConfig = {
    ...TabNavigator.Presets.AndroidTopTabs,
    animationEnabled: false,
    swipeEnabled: true,
}

const MainTab = StackNavigator({
  Home: {
    screen: TabNavigator(HomeTabs, mainTabConfig),
    path: '/',
    navigationOptions: {
      title: 'Welcome',
    //   headerMode: 'none'
    },
  },
  Profile: {
    screen: MyProfileScreen,
    path: '/people/:name',
    navigationOptions: ({ navigation }) => ({
      title: `${navigation.state.params.name}'s Profile!`,
    }),
  },
}, stackConfig);

const SettingsTab = StackNavigator({
  Settings: {
    screen: MySettingsScreen,
    path: '/',
    navigationOptions: () => ({
      title: 'Settings',
    }),
  },
  NotifSettings: {
    screen: MyNotificationsSettingsScreen,
    navigationOptions: {
      title: 'Notification Settings',
    },
  },
}, stackConfig);

class StacksInTabs extends Component {
    constructor (props) {
        super(props);

        this.state = { softKeys: false }
    }

    componentDidMount () {
        if (Platform.OS === 'android') {
            DetectNavbar.hasSoftKeys().then((softKeys) => {
              this.setState({ softKeys });
            });
        }
    }

    render () {
        let tabBarConfig = {
          tabBarPosition: 'bottom',
          animationEnabled: false,
          swipeEnabled: false
      };

      if (Platform.OS === 'android') {
          tabBarConfig.tabBarComponent = NavigationComponent;
          tabBarConfig.tabBarOptions = {
              bottomNavigationOptions: {
                  style: {
                      height: this.state.softKeys ? 104 : 56
                  },
                  innerStyle: {
                      paddingBottom: this.state.softKeys ? 48 : 0
                  },

              }
          }
      }
        const Tabs = TabNavigator({
          MainTab: {
            screen: MainTab,
            path: '/',
            navigationOptions: {
              tabBarLabel: 'Home',
              tabBarIcon: ({ tintColor, focused }) => (
                <Ionicons
                  name={focused ? 'ios-home' : 'ios-home-outline'}
                  size={26}
                  style={{ color: tintColor }}
                />
              ),
            },
          },
          SettingsTab: {
            screen: SettingsTab,
            path: '/settings',
            navigationOptions: {
              tabBarLabel: 'Settings',
              tabBarIcon: ({ tintColor, focused }) => (
                <Ionicons
                  name={focused ? 'ios-settings' : 'ios-settings-outline'}
                  size={26}
                  style={{ color: tintColor }}
                />
              ),
            },
          },
      }, tabBarConfig);

        return <Tabs />
    }
}

export default StacksInTabs;
dabit3 commented 7 years ago

I had a similar issue, I fixed it by making sure my containers were either the height of the device or a flex:1, have you given this a shot?

buncis commented 7 years ago

adding lazy: true to the TabNavigatorConfig fix this issue

here is my full AppNavigator

import React from 'react';
import { StackNavigator,TabNavigator } from 'react-navigation';
import Screen1 from './screens/Screen1'
import Screen2 from './screens/Screen2'
import Screen3 from './screens/Screen3'
import Screen21 from './screens/Screen21'
import Screen22 from './screens/Screen22'

const Tab2 = TabNavigator ({
  Screen2: { screen: Screen2 },
  Screen21: { screen: Screen21 },
  Screen22: { screen: Screen22 }
})

const Tab1 = TabNavigator ({
  Screen1: { screen: Screen1 },
  Screen2: { screen: Tab2 },
  Screen3:{ screen: Screen3 }
}, {
  tabBarPosition: 'bottom',
  lazy: true  /*Hey look at here Lazy is true*/
});

const AppNavigator = StackNavigator({
    Tab1: { screen: Tab1 }
});

export default AppNavigator;
viruhemanth commented 7 years ago

@dabit3 thanks mate it worked for me!!

giedriusr commented 7 years ago

I think that the first navigator must be stack navigator, though, nesting tab navigators worked for me on iOS, but not on Android. So probably both ways (stack + tab & tab + tab) should work, but I couldn't make it work when Tab + Tab is used.

sebringj commented 7 years ago

The way I overcame this was a hack but works...

componentDidMount() { setTimeout(() => { this.refs.scrollView.scrollTo({ x: 0, y: 1, animated: false }) }, 100) }

where you do a timeout after mount and scroll

douglasjunior commented 7 years ago

The @sebringj solution worked for me. My problem only occurs on iOS. I have a ListView inside the Tab, if I do Scroll manually or programmatically the content appears.

tanaytanay commented 7 years ago

@sebringj I had the exact same problem. I used https://github.com/react-community/react-navigation/issues/1238#issuecomment-297213802

douglasjunior commented 7 years ago

Sorry, I did not come back here to mention, but after some tests I set up the removeClippedSubviews to switch between true and false.

When the DataSource updates:

this.setState({ removeClippedSubviews: false });

When the user starts the scroll.

this.setState({ removeClippedSubviews: true });

So I can maintain a good performance with the list.

spencercarli commented 7 years ago

Hi! In an effort to get the hundreds of issues on this repo under control I'm closing issues tagged as questions. Please don't take this personally - it's simply something we need to do to get the issues to a manageable point. If you still have a question I encourage you to re-read the docs, ask on StackOverflow, or ask on the #react-navigation Reactiflux channel. Thank you!

gananggww commented 6 years ago

solved!, dont use View Tag!

blank screen (before solved):

import { TabNavigator } from 'react-navigation';

const Navigator = TabNavigator({
  Card: {
    screen: Card
  },
  List: {
    screen: Lists
  }
});
export default class App extends Component<{}> {
  render() {
    return (
       <View>
         <Navigator/>
       </View>
    );
  }
}

SOLVED ! :

import { TabNavigator } from 'react-navigation';

const Navigator = TabNavigator({
  Card: {
    screen: Card
  },
  List: {
    screen: Lists
  }
});
export default class App extends Component<{}> {
  render() {
    return (
        <Navigator/>
    );
  }
}
Chathula commented 6 years ago

This is what i am using.. yet i couldn't fix this issue on android!

import React, { Component } from 'react';
import { View, ScrollView, StatusBar } from 'react-native';
import ImageSlider from 'react-native-image-slider';

import { MainTabNavigator } from './config/routes';
import { STATUS_BAR_HEIGHT } from './constants';

import Header from './components/header';

export default class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            position: 0,
            interval: null
        };
    }

    componentWillMount() {
        this.setState({ interval: setInterval(() => {
            this.setState({ position: this.state.position === 3 ? 0 : this.state.position + 1 });
        }, 2000) });
    }

    componentWillUnmount() {
        clearInterval(this.state.interval);
    }

    render() {
        return (
            <ScrollView style={styles.scrollContainer}>
                <StatusBar
                    backgroundColor="#000"
                    barStyle="dark-content"
                    translucent={false}
                />
                <Header />
                <View style={styles.imageContainer}>
                    <ImageSlider
                        images={[
                            'http://placeimg.com/640/480/any',
                            'http://placeimg.com/640/480/any',
                            'http://placeimg.com/640/480/any',
                            'https://images.pexels.com/photos/216355/pexels-photo-216355.jpeg?w=940&h=650&auto=compress&cs=tinysrgb',
                        ]} 
                        position={this.state.position}
                        onPositionChanged={position => this.setState({ position })} 
                    />
                </View>

                <MainTabNavigator />     
            </ScrollView>
        );
    }
}

const styles = {
    scrollContainer: {
        flex: 1,
        marginTop: STATUS_BAR_HEIGHT
    },
    navContainer: {
        flex: 1,
    },
    imageContainer: {
        flex: 1,
        flexDirection: 'row',
        alignItems: 'stretch'
    },
};

routes

import { StackNavigator, TabNavigator, TabBarTop } from 'react-navigation';

import HomeScreen from '../screens/HomeScreen';

export const MainNavigator = StackNavigator({
    Home: {
        screen: HomeScreen,
    }
});

export const MainTabNavigator = TabNavigator({
    Home: {
        screen: HomeScreen,
        navigationOptions: () => ({
            tabBarLabel: 'Browse',
        })
    },
    Store: {
        screen: HomeScreen,
    },
    Profile: {
        screen: HomeScreen,
    }
}, {
    initialRouteName: 'Home',
    tabBarPosition: 'top',
    swipeEnabled: true,
    tabBarComponent: TabBarTop,
    tabBarOptions: {
        allowFontScaling: false,
        style: {
            backgroundColor: 'red',
            padding: 8,
        },
        labelStyle: {
            color: 'white',
            fontSize: 12,
        },
        indicatorStyle: {
            borderBottomColor: '#ffffff',
            borderBottomWidth: 3,
        },
    }
});
Chathula commented 6 years ago

just fixed using contentContainerStyle={{ flex: 1 }} in scrollView

pallavbakshi commented 6 years ago

Hey! Did anyone solve this issue?

ou2s commented 6 years ago

I have the same issue by stacking stack navigators and only one tab based navigator. The issue appears very randomly....

Dezainer commented 6 years ago

Setting lazy true on the TabNavigator did the trick for me, but the bug only appears when i hide the tabBar on the nested navigator... Really weird

JanithaR commented 6 years ago

For me the issue is that I have the TabNavigator inside a ScrollView.

EDIT : Annoyingly after trying many things like @keeleycarrigan has mentioned setting both swipeEnabled and animationEnabled to false makes work on both platforms equally.

f4z3k4s commented 6 years ago

I had an issue on Android with nested navigators interestingly only on release builds. My TabNavigator was nested in a StackNavigator. Tab A mounted correctly, but Tab B and Tab C never called render(), meaning that these screens never even mounted when I navigated to them, I obviously only got blank screens. I managed to solve the issue with adding an extra screen that returns <TabNavigator screenProps={{ rootNavigation: navigation }} /> instead of using TabNavigator itself as a a screen like : screen: MainTabNavigator. It works fine even without using swipeEnabled: false, animationEnabled: false, lazyLoad: true in my MainTabNavigator.

Package versions:

    "react": "16.2.0",
    "react-native": "0.52",
    "react-navigation": "1.1.2",

My setup looks like this:

Top level StackNavigator

export default StackNavigator(
  {
    Splash: { screen: SplashScreen, path: 'Splash' },
    Login: { screen: LoginScreen, path: 'Login' },
    // Home: { screen: MainTabNavigator, path: 'Home'  }, -> replace this line with next line
    Home: { screen: HomeScreen, path: 'Home' }, // prevent nested navigator bugs
    WebView: { screen: WebViewScreen, path: 'WebViewCards' },
    SubscriptionDetails: { screen: SubscriptionDetailsScreen, path: 'SubscriptionDetails' },
  },
  {
    headerMode: 'none',
    navigationOptions: {
      headerVisible: false,
    },
  },
);

MainTabNavigator

export const MainTabNavigator = TabNavigator({
  MainCards: { screen: cardsNavigator }, // stack navigator
  MainCollections: { screen: collectionsNavigator }, // stack navigator
  Profile: { screen: profileNavigator }, // stack navigator
}, {
  tabBarPosition: 'bottom',
  tabBarOptions: {
    activeTintColor: Platform.OS === 'android' ? colors.greySuperdark : colors.iOSBlue,
    inactiveTintColor: colors.greySuperdark,
    style: { backgroundColor: 'white' },
  },
});

create HomeScreen after replacing screen: MainTabNavigator with screen: HomeScreen

HomeScreen

import React, { Component } from 'react';
import { MainTabNavigator } from './location/to/file';

export default class HomeScreen extends Component {

  static navigationOptions = ({ navigation }) => {
    return {
      title: 'Home',
      headerStyle: {
        backgroundColor: 'white',
      },
      headerBackTitle: null,
    };
  };

  render() {
    const { navigation } = this.props;
    return <MainTabNavigator screenProps={{ rootNavigation: navigation }} />;
  }
}

I don't like to spend more time on investigating the cause of this behaviour, I hope this workaround helps out somebody, I use several more nested TabNavigators this way and all of them are working fine. :)

robvolk commented 6 years ago

After playing around with countless permutations of seemingly unrelated config settings, here's how I fixed the blank tabs issue:

const TabBar = TabNavigator(
  {
    Tab1: { screen: Tab1 },
    Tab2: { screen: Tab2 },
  },
  { 
    swipeEnabled: true, // fixes a bug in react navigation
    lazy: false, // fixes a bug in react navigation
  }
);
Syahrul commented 6 years ago

Solution from @robvolk seems to fix the blank tab screen for me as well. I'm using 1.2.0. swipeEnabled: true & lazy: false must be specify.

f4z3k4s commented 6 years ago

@robvolk thanks for experimenting, it indeed solves the problem on v1.1.2.

gentlee commented 6 years ago

Only way for me to fix it is to downgrade to 1.0.0-beta15. It occurs with RTL enabled.

npm i react-navigation@1.0.0-beta.15

@spencercarli reopen this issue and set label bug.

brentvatne commented 6 years ago

@Gentlee - can you create a new issue that follows the issue template and describes the problem concisely? I would be happy to look at it if so!

AntoineChwat commented 6 years ago

If you're working with redux, the issue might be about correctly applying the middleware. I for one did create it using

createReactNavigationReduxMiddleware(
  'root',
  state => state.nav,
);

But I never applied it to my store! Hence your code for the store should look something like:

const navMiddleware = createReactNavigationReduxMiddleware(
  'root',
  state => state.nav,
);
const addListener = createReduxBoundAddListener('root');

const store = createStore(
  rootReducer,
  applyMiddleware(navMiddleware)
);

Simply applying the middleware fixed the issue for me, no need to set lazy: false and swipeEnabled: true anymore (although that technique worked as well for me)

itsFindable commented 6 years ago

@robvolk thank you!!! Setting lazy:false and swipeEnabled:true solved my issue. I wish I had read your notes exactly 3 hours ago!

mjroeleveld commented 6 years ago

As @AntoineChwat indicates it's probably due to a misconfiguration of Redux middleware. I returned the incorrect state sub property in the second argument of createReactNavigationReduxMiddleware.

midoalone commented 6 years ago

@f4z3k4s many thanks your solution works with me

jtich commented 6 years ago

Solved this problem using a combination of @gananggww and @Chathula's recommendations:

  1. Ensure your Navigation component is nested within a ScrollView (not View)
  2. Add contentContainerStyle={{ flex: 1 }} to your ScrollView
chengsam commented 6 years ago

@robvolk solution worked for me but what if i really want to disable swipe?

hzuhyb commented 6 years ago

@robvolk solution worked for me but what if i really want to disable swipe?

robvolk commented 6 years ago

Since v2.x is out, I'd try upgrading and see if the problem has been fixed. I've removed tabs from my UI for unrelated reasons, so I can't say for sure.

lylest commented 5 years ago

I had a similar issue, I fixed it by making sure my containers were either the height of the device or a flex:1, have you given this a shot?

this worked for me

lylest commented 5 years ago

on App.js const styles = StyleSheet.create({ container: { flex:1, },

mitchobrian commented 2 years ago

I had a similar error with Expo SDK 44 and and outdated react-native 0.64.1 - after upgrading to 0.66.4 the Stack Screens appears.

github-actions[bot] commented 2 years ago

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

EmaX093 commented 2 years ago

I had a similar error with Expo SDK 44 and and outdated react-native 0.64.1 - after upgrading to 0.66.4 the Stack Screens appears.

I'm using EXPO SDK 43, this works for me! Thank you good man.

github-actions[bot] commented 2 years ago

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.