Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DrawerNavigator screen shouldn't unmount #2277

Closed
vanBrunneren opened this issue Jul 31, 2017 · 25 comments
Closed

DrawerNavigator screen shouldn't unmount #2277

vanBrunneren opened this issue Jul 31, 2017 · 25 comments
Assignees

Comments

@vanBrunneren
Copy link

Hi at all, maybe it's a misunderstanding or just my fault but I googled for hours, I made a StackOverflow post and I wrote on Discord but no one seems to have a solution to my problem.

Current Behavior

I built a very simple example which shows my problem. I have a DrawerNavigator with 2 Screens

class HomeView extends Component {

	componentDidMount() {
		console.log("mount home");
	}

	render() {

		return(
			<View>
				<Text>HomeView</Text>
			</View>
		);

	}

}

class SecondView extends Component {

	componentDidMount() {
		console.log("mount second");
	}

	render() {

		return(
			<View>
   		                 <Text onPress={ () => this.props.navigation.navigate('HomeView') }>SecondView</Text>
			</View>
		);

	}

}

const MyNavigator = DrawerNavigator({

	HomeView: {
		screen: HomeView
	},

	SecondView: {
		screen: SecondView
	}

});

My steps:

  1. The HomeView gets mounted
  2. I open the drawer
  3. I press on Second View
  4. Second View gets mounted
  5. I press on "Second View"
  6. It navigates to HomeView and the componentDidMount function gets called

Giphy Problem

Expected Behavior

I'm expecting the HomeView should stay mounted. If I'm calling this.props.navigation.navigate('HomeView') I'm expecting my app should show the HomeView Screen as I had it before leaving it. Am I the only one who's facing this issue? Or is my concept completely wrong and someone can tell me a better way to do this? This is a very basic example but think of I'm having a Drawer and each object is a StackNavigator with multiple subroutes. Consider to go navigate 3 times in the first stack, then call another Route from the Drawer and then going back to the first route. You don't want to rerender the whole Stack....

I'm lucky with any suggestions because I'm getting tired of try and fail

Your Environment

software version
react-navigation 1.0.0-beta.11
react-native 0.46.4
node 8.0.0
npm or yarn yarn 0.24.6
@vonovak
Copy link
Member

vonovak commented Aug 29, 2017

why do you not call goBack()?

@vanBrunneren
Copy link
Author

@vonovak because I don't want to rerender my screen when I press the Drawer.

In the mean time nobody has answered or helped me out I started to dig and search for a solution to my problem. Now I have a kind of solution but it seems not to work so I created a detailed new issue with my newest try at #2495

As this is not longer a problem for me, because I found a kind of workaround, the issue can be closed. But I'm still interested in finding a solution or the answer that is is an antipattern or something like this

@vonovak
Copy link
Member

vonovak commented Aug 30, 2017

@vanBrunneren my guess would be that goBack() does not cause a re-render, but I have not tested it

@vanBrunneren
Copy link
Author

@vonovak oh sorry I was i bit confused at the moment. Of course you are right goBack() does not a rerender. But I have multiple routes in my stack and I want to jump maybe from the 6th to the 3th and this is not realizable with a goBack(). And If I do a goBack() the route which I press goBack() on will get unmounted. And when I go from maybe the 5th route to the 7th and then switch again to the 5th the 5th would be unmounted and has to mount again. This is not what I want, I want a function like "jumpTo" where you can jump to the routes in the stack

@nhuthan
Copy link

nhuthan commented Oct 13, 2017

+1
same problem
if you go to B from A, A will be unmounted and changed states of A will be lost. I dont want that.

@kelset
Copy link

kelset commented Nov 25, 2017

Pinging OP @vanBrunneren since this issue has not been active for a while, and it's related to an old version of the lib.

Please let me know if you want this to remain open; if I get no answer in the next 7 days I will close it.

@vanBrunneren
Copy link
Author

@kelset the issue is still persisting. I've updated the library to the newest version and the same thing still happens. Is this even a case someone will ever fix? Or let's ask the other way around, what is the default way to do this? Are @nhuthan and I the only ones who are trying to do this?

@kelset
Copy link

kelset commented Nov 27, 2017

Ok, thanks for letting me know.

Are @nhuthan and I the only ones who are trying to do this?

Maybe you are the only ones trying to achieve this without a state manager (redux or MobX)?

Is this even a case someone will ever fix?

Can't really tell atm. The Drawer is a "portion" of the navigation I personally don't use so I don't have much knowledge of best practices (I know @GantMan was writing some articles/tutorials which you can find linked in the README); and I think this issue, in particular, seems quite "low priority" in the grand scheme. But nothing is set in stone.

@amirmatin
Copy link

@vanBrunneren Did you manage to find a simple solution for the Drawer nav problem ? I am facing the same issue and it is really irritating when there is no straight solution for a problem like this ...

@vanBrunneren
Copy link
Author

@amirmatin no not at the moment, I didn't had the time at the moment to work on this, as soon as I have more time I definitely give this another effort

@amirmatin
Copy link

@vanBrunneren thanks for your comment.. I am giving this other lib called "react native navigation" a shot and will let you know if I had any luck .. from their example it seems that they don't have the re-mounting issue ..

@Jaftem
Copy link

Jaftem commented Dec 29, 2017

+1. I would also like to see this implemented. If you have a very 'heavy' screen, you shouldn't be forced to re-render it every time.

@amirmatin
Copy link

@Jaftem I am using this other lib called "react native navigation" which doesnt have this issue.. It has less documentation but the example they provide is really all you would need ..

@esipavicius
Copy link

Guys, I found solution how do not unmount.

First of all StackNavigator must be in DrawerNavigator. At this point changing screens from DrawerNavigator it not run componentWillUnmount method.

`Example

const HomeNavigator = StackNavigator({
    Home: {
        screen: HomeScreen,
    },
    OrderScreen: {
        screen: OrderScreen,
    },
    ReminderScreen: {
        screen: ReminderScreen,
    },
    BonusScreen: {
        screen: BonusScreen,
    },
    RegisterNewAutoScreen: {
        screen: RegisterNewAutoScreen,
    },
}, {});

const HomeNavigationDrawer = DrawerNavigator({
    HomePage: {
        screen: HomeNavigator,
    },
}, {
    contentComponent: SideMenu,
    drawerOpenRoute: 'DrawerOpen',
    drawerCloseRoute: 'DrawerClose',
    drawerToggleRoute: 'DrawerToggle',
    drawerWidth: 300,
});

`

At this point going back it keeps previous screen. But you should know it re runs constructor, componentWillMount and componentDidMount methods when selected screen from menu. When loads menu and to header shows <- back arrow. When you click it - runs componentWillUnmount on active scrren and it is not more available to load prevous state. More abount navigation actions can read at: https://reactnavigation.org/docs/navigators/navigation-actions

`SideMenu

 class SideMenu extends Component {
   /**
    * Navigate between screens action
    */
   navigateToScreen = (route) => () => {
       let navigateAction = NavigationActions.navigate({
           routeName: route,
       });

       this.props.navigation.dispatch(navigateAction);
   }

   /**
    * Logout from firebase
    */
   onPressLogout() {
       Firebase.logout();
   }

   /**
    * Render side menu
    */
   render () {
       return (
           <View style = { styles.container } >
               <ScrollView>
                   <View>
                       <Text>{ Firebase.user.email }</Text>
                       <View style = { styles.navSectionStyle } >
                           <TouchableHighlight onPress={ this.navigateToScreen('ReminderScreen') }>
                               <View style={{ flexDirection: 'row' }}>
                                   <MaterialCommunityIcons
                                       name="camera-timer"
                                       backgroundColor="transparent"
                                       color={ COLORS.LIGHT_SILVER }
                                       size={ 30 }
                                   />
                                   <Text>Priminimai</Text>
                               </View>
                           </TouchableHighlight>
  
                           <TouchableHighlight onPress={ this.navigateToScreen('OrderScreen') }>
                               <View style={{ flexDirection: 'row' }}>
                                   <MaterialCommunityIcons
                                       name="cart"
                                       backgroundColor="transparent"
                                       color={ COLORS.LIGHT_SILVER }
                                       size={ 30 }
                                   />
                                   <Text>Užsakymai</Text>
                               </View>
                           </TouchableHighlight>
   
                           <TouchableHighlight onPress={ this.navigateToScreen('BonusScreen') }>
                               <View style={{ flexDirection: 'row' }}>
                                   <Entypo
                                       name="ticket"
                                       backgroundColor="transparent"
                                       color={ COLORS.LIGHT_SILVER }
                                       size={ 30 }
                                   />
                                   <Text>Bonusai</Text>
                               </View>
                           </TouchableHighlight>

                           <TouchableHighlight onPress={ this.navigateToScreen('BonusScreen') }>
                               <View style={{ flexDirection: 'row' }}>
                                   <Entypo
                                       name="shop"
                                       backgroundColor="transparent"
                                       color={ COLORS.LIGHT_SILVER }
                                       size={ 30 }
                                   />
                                   <Text>Mano įmonės</Text>
                               </View>
                           </TouchableHighlight>

                           <TouchableHighlight onPress={ this.onPressLogout.bind(this) }>
                               <View style={{ flexDirection: 'row' }}>
                                   <Entypo
                                       name="log-out"
                                       backgroundColor="transparent"
                                       color={ COLORS.LIGHT_SILVER }
                                       size={ 30 }
                                   />
                                   <Text>Atsijungti</Text>
                               </View>
                           </TouchableHighlight>
                       </View>
                   </View>
               </ScrollView>
           </View>
       );
   }
 }

SideMenu.propTypes = {
     navigation: PropTypes.object,
};
 
export default SideMenu;

`

I think it helps for someone :)

@jaltin
Copy link

jaltin commented Apr 5, 2018

I have also ran into this issue and it's a pain.

I swapped over to react-native-navigation as suggested by @amirmatin and it initially worked well. However I ran into a bunch of limitations/bugs with that component and I am therefore hoping that there will soon be a fix to the unmount problem in react-navigation so I can go back to using it.

@brentvatne
Copy link
Member

I agree this behavior should not be the default for drawer.. I think it should work just like tabs.

@brentvatne brentvatne self-assigned this Apr 5, 2018
@vonovak
Copy link
Member

vonovak commented Apr 6, 2018

possibly (?) related: #3164

@jaltin
Copy link

jaltin commented May 4, 2018

@brentvatne Do you know if this is going to be addressed to work just like tabs? And if so, is there any timeline for when it might be looked at?

Thx!

@riwu
Copy link

riwu commented May 4, 2018

@brentvatne Please add an option to toggle between the two behaviors instead of replacing the current behavior entirely.
Ideally, add a unmountAfterSwitch property in RouteConfigs so that we can customise the behavior for different screens; eg. set unmountAfterSwitch: true for rarely accessed screens so that they are not kept in memory if the user only switched to it once.

@brentvatne
Copy link
Member

@jaltin - not until after we release 2.0. you could fork drawernavigator and make your own version though, eg: https://github.com/react-europe/react-europe-2018-mobile/blob/master/src/Navigation.js

@jaltin
Copy link

jaltin commented May 4, 2018

Thanks for the update @brentvatne.

Do you have any idea of how long it might be before 2.0 is released?

@brentvatne
Copy link
Member

maybe next week but anyways if you need this now you should just make your own drawer navigator

@poptocrack
Copy link

poptocrack commented Jun 20, 2018

So, since the drawer goBack() is broken / no working as intended, I've tried to find a work around. Here it is, combining drawerNavigator and StackNavigator:

// this const is here only to make thing easier to maintain, no duplicate declarations
// routes here will appear in drawer
const routes = { 
  RemotePage: { screen: RemotePage },
  ReferralPage: { screen: ReferralPage },
  TestPage: { screen: ReferralPage },
}

// StackNavigator with all you routes so you can navigate easely between them
const stack = createStackNavigator({
  Home: { screen: HomePage },
  OwnerPage: { screen: OwnerPage },
  ...routes,
}, {
  initialRoute: 'Home'
})

// DrawerNavigator and main menu on the left.
// navigationOptions are only here if you want to disable drawer in child screens,
// feel free to remove it
export default createDrawerNavigator({
  Home: { screen: stack,
    navigationOptions: ({ navigation }) => ({ 
      drawerLockMode: 'locked-closed',
    }),
  },
  ...routes,
});

This solution is quite easy to implement.
With this, the goBack() button is working as intended, and the home page is not re-mounting.

I'm using react-navigation "react-navigation": "^2.3.1", tested wit expo SDK 28 on iOS and Android.

@brentvatne Do you validate this kind of use or is it not intended and will be fixed in the future?

Here's a working Snack

@brentvatne
Copy link
Member

@poptocrack - #4474

@brentvatne
Copy link
Member

@react-navigation react-navigation locked and limited conversation to collaborators Jun 20, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests