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

Reset action does not seem to work #59

Closed
markflorisson opened this issue Jan 28, 2017 · 35 comments
Closed

Reset action does not seem to work #59

markflorisson opened this issue Jan 28, 2017 · 35 comments

Comments

@markflorisson
Copy link

I'm trying to reset the stack in a StackNavigation using:

        dispatch({
            type: 'Reset',
            actions: [{
                type: 'Navigate',
                routeName: 'Home',
            }],
        })

However, this gives an error: "There should always be only one scene active, not 0".

If I try to dispatch() the reset action, followed by a navigate(), then it works but it does not "forget" any part of the history stack. How can I reset the stack?

@JustinNothling
Copy link

Hey Mark, I got the same problem on my end so I'll let you know if I come up with a solution.

@JustinNothling
Copy link

JustinNothling commented Jan 28, 2017

Ok So I have a solution:

You need to add an index of 0 to your reset action:
dispatch({type: 'Reset', index: 0, actions: [{ type: 'Navigate', routeName:'Home'}]})

Haven't yet figured out how to run this only at the end of a transition - think you may need to use the Transitioner component

@markflorisson
Copy link
Author

Awesome, that works! Maybe it would be useful to have a 'reset' function on the 'navigation' object?

@ericvicenti
Copy link
Contributor

@JustinNothling, good point, we don't expose Transitioner's onTransitionEnd through CardStack or StackNavigator. That hasn't come up yet! Want to submit a PR?

I'm on the fence about adding a reset helper because it doesn't always make sense for a child component to be aware of the application around it. Pretty soon people will want to add popAndReplace and pushTwiceAndJumpBack or whatever :-) . The replace option won't always be practical, because the screen can't see the whole navigation state (nor should it! you don't want every screen to re-render when one of their state changes). Instead, to support complicated navigation behaviors, you may want to override behavior on the router: https://reactnavigation.org/docs/routers/#Custom-Navigation-Actions

@JustinNothling
Copy link

@ericvicenti we're in agreement about the reset helper.

How would you go about structuring a simple login/logout using react-navigation? At the moment pushing a home route onto login allows users to goBack to login using gestures (which isn't ideal). Would you reset the stack or would you prevent the gesture?

@nebula-ai
Copy link

You might fine this useful. It is a variation of the tutorial where I have a login screen and then a logout function under the Options right element of the RecentChatsScreen which will get you back to the original login screen.

import React from 'react';
import {
AppRegistry,
Text,
View,
Button
} from 'react-native';
import { StackNavigator, TabNavigator } from 'react-navigation';

class SigninScreen extends React.Component {
static navigationOptions = {
title: 'Sign In',
};
render() {
const { navigate } = this.props.navigation;
return (

Sign In
<Button
onPress={() => navigate('Home')}
title="Sign In"
/>

);
}
}

class OptionsScreen extends React.Component {
static navigationOptions = {
title: 'Options',
};
render() {
const { navigate } = this.props.navigation;
const { dispatch } = this.props.navigation;
return (

Options!
<Button
onPress={() => {dispatch({type:'Reset', actions: [{ type: 'Navigate', routeName: 'Signin'}], index: 0 })}}
title="Logout"
/>

);
}
}

class RecentChatsScreen extends React.Component {
static navigationOptions = {
// Nav options can be defined as a function of the navigation prop:

header: (navigation) => ({
  title: 'Recent Chats',
  right: <Button title='Options' onPress={() => navigation.navigate('Options')} />
}),

};

render() {
return (

List of recent chats
<Button
onPress={() => this.props.navigation.navigate('Chat', { user: 'Lucy' })}
title="Chat with Lucy"
/>

)
}
}

class AllContactsScreen extends React.Component {
render() {
return (

List of recent chats
<Button
onPress={() => this.props.navigation.navigate('Chat', { user: 'Jane' })}
title="Chat with Jane"
/>

)

}
}

const MainScreenNavigator = TabNavigator({
Recent: { screen: RecentChatsScreen },
All: { screen: AllContactsScreen },
});

MainScreenNavigator.navigationOptions = {
title: 'My Chats',
};

class ChatScreen extends React.Component {
static navigationOptions = {
// Nav options can be defined as a function of the navigation prop:
title: ({ state }) => Chat with ${state.params.user},
};
render() {
// The screen's current route is passed in to props.navigation.state:
const { params } = this.props.navigation.state;
return (

Chat with {params.user}

);
}
}

const TheApp = StackNavigator({
Signin: { screen: SigninScreen},
Home: { screen: MainScreenNavigator },
Chat: { screen: ChatScreen },
Options: { screen: OptionsScreen },

});

AppRegistry.registerComponent('exp', () => TheApp);

@ericvicenti
Copy link
Contributor

@JustinNothling, I would probably want to prevent the gesture. But I don't think there is a way to do that currently.

It might make sense to add a gesturesEnabled screen navigation option, so a screen can toggle gestures based on the route.

@CoteViande
Copy link

I have tried to implement Login/Logout using Reset action. However, with NestedNavigators I get the following error:

There is no route defined for key Home. Must be one of: 'FacebookAuthentication','EmailAuthentication'

I run the redux implemantation, like in the docs, so in a middleware, I run:

// middleware
if (prevState.isAuthenticated !== nextState.isAuthenticated) {
    const nextScreen = nextState.isAuthenticated
      ? 'Home'
      : 'Authenticate'
    dispatch({
      type: 'Reset',
      index: 0,
      actions: [{
        type: 'Navigate',
        routeName: nextScreen,
      }],
    })
  }

Here is my reduced Navigator config:

// Navigator settings
const AuthenticateNavigator = StackNavigator({
  FacebookAuthentication: { screen: FacebookAuthScreen },
  EmailAuthentication: { screen: EmailAuthScreen },
})

const MainScreenNavigator = TabNavigator({
  Recent: { screen: RecentScreen },
  Logout: { screen: LogoutScreen },
})

const AppNavigator = StackNavigator({
  Authenticate: { screen: AuthenticateNavigator },
  Home: { screen: MainScreenNavigator },
})

@markflorisson
Copy link
Author

Actually, the reset action doesn't seem to work properly. If I reset to 'Home' it works, but if I add additional actions to the reset it doesn't navigate to the right screen and seems to "freeze" (no button presses get registered). This is the code:

        dispatch({
            type: 'Reset',
            index: 0,
            actions: [
                {
                    type: 'Navigate',
                    routeName: 'Home',
                },
                {
                    type: 'Navigate',
                    routeName: 'Items',
                },
            ],
        })

This is the state:

{ action: 
  { type: 'Reset',
    index: 0,
    actions: 
     [ { type: 'Navigate', routeName: 'Home' },
       { type: 'Navigate', routeName: 'Items' } ] },
 newState: 
  { index: 0,
    routes: 
     [ { routeName: 'Home', key: 'Init0' },
       { routeName: 'Items', key: 'Init1' } ] },
 lastState: 
  { index: 3,
    routes: 
     [ { routeName: 'Home', key: 'Init' },
       { type: 'Navigate',
         routeName: 'Items',
         params: undefined,
         action: undefined,
         key: 'id-1485774310007-0' },
       { type: 'Navigate',
         routeName: 'Menu',
         params: undefined,
         action: undefined,
         key: 'id-1485774310007-1' },
       { type: 'Navigate',
         routeName: 'Checkout',
         params: undefined,
         action: undefined,
         key: 'id-1485774310007-2' } ] } }

@markflorisson
Copy link
Author

The workaround that works for me is to set up deep linking and use Linking.openURL(...). However, this will only work well if you are happy with a navigation stack of 1 level deep (where the back arrow points back to home).

@CoteViande
Copy link

CoteViande commented Jan 31, 2017

I fixed my issue by restraining myself to only one level of depth in my navigation, without deep linking.

const MainScreenNavigator = TabNavigator({
  Recent: { screen: RecentScreen },
  Logout: { screen: LogoutScreen },
})

const AppNavigator = StackNavigator({
  FacebookAuthentication: { screen: FacebookAuthScreen },
  EmailAuthentication: { screen: EmailAuthScreen },
  Home: { screen: MainScreenNavigator },
})

@lslima91
Copy link

lslima91 commented Jan 31, 2017

Maybe I'm misunderstanding, but shouldn't it be possible to simply dispatch a goBack, if you want a simple pop?
{type: 'goBack', ...}

Or do we need to manually dispatch reset actions in this case?

@elliottjro
Copy link

elliottjro commented Jan 31, 2017

@lslima91 a goBack() only works if you navigated into the second screen. In the case where eg: user was already logged in and they press logout -> this should call reset and not goBack because we never went from logged out screen to home screen.

-> At least i think this is the functionality of goBack() please correct if I'm wrong

ps: I am also running into this issue, it looks like a second action cannot be sent to the child router.

@lslima91
Copy link

lslima91 commented Feb 1, 2017

@elliottjro The thing is, I'm not able to dispatch a type: goBack action. I have to manually dispatch a reset with the correct routename.

@elliottjro
Copy link

@lslima91 the goBack() method isn't an available type to be sent via dispatch(). You have to call goBack() directly on the navigation prop. reference

Example:

<Button onPress={ () => this.props.navigation.goBack() } />

@lslima91
Copy link

lslima91 commented Feb 2, 2017

nvm, I have to dispatch {type:'Back'}, this new PR makes things much better imo #120

@JustinNothling
Copy link

JustinNothling commented Feb 2, 2017

Hey has anyone got the new code on for reset in the docs to work?
https://reactnavigation.org/docs/navigators/navigation-prop

import { NavigationActions } from 'react-navigation'

NavigationActions.reset({
  actions: NavigationActions.navigate({ routeName: 'Profile'}),
})

I'm getting an error saying reset is undefined.

@satya164
Copy link
Member

satya164 commented Feb 2, 2017

@JustinNothling are using the version from master?

@mmazzarolo
Copy link

mmazzarolo commented Feb 2, 2017

@JustinNothling I have, but I had to tweak a bit the code...

@satya164 I'm on master and by reading the docs it seems that for dispatching a reset action I need all I have to do is the following:

const actionToDispatch = NavigationActions.reset({
  actions: NavigationActions.navigate({ routeName })
})
this.props.navigation.dispatch(actionToDispatch)

Which doesn't work.
The following does though:

const actionToDispatch = NavigationActions.reset({
  index: 0,
  actions: [NavigationActions.navigate({ routeName })] // Array!
})
this.props.navigation.dispatch(actionToDispatch)

Is this the correct way to reset the stack? Am I just misunderstanding the docs? Let me know, I'd like to PR with a "clear way" for resetting the stack.

@satya164
Copy link
Member

satya164 commented Feb 2, 2017

the second one is correct. seems there's an error in the doc. willing to send a PR?

@rohit-ravikoti
Copy link

@mmazzarolo,
this.props.navigation.dispatch doesn't seem to be doing anything on my app. To give some context, I didn't integrate react-navigation with a redux store.

@mmazzarolo
Copy link

mmazzarolo commented Feb 2, 2017

Copy-paste from #156

@rohit-ravikoti the snippet you pasted is not using redux, it is just plain react-navigation (which uses a pattern similar to the one used in redux).
I'm using it right now and it is working correctly.

Make sure you're using the master branch though!

  1. Remove your current version of react-navigation: npm uninstall react-navigation
  2. Add the master branch one: npm install --save https://github.com/react-community/react-navigation.git
  3. Restart the packager npm run start -- --reset-cache
  4. Fire up your emulator

Let me know if it works :)

@rohit-ravikoti
Copy link

That worked! Thanks! 😜

ericvicenti pushed a commit that referenced this issue Feb 2, 2017
- Added the index
- Actions is now an array
Related to #59 (comment)
@elliottjro
Copy link

Hey all, it looks like this issue is fixed but original issue was using the reset action along with a nested navigation action.

@markflorisson can you confirm this is the case else close current issue.

@markflorisson
Copy link
Author

Hi @elliottjro, yeah the refactor looks great but nested navigation actions still don't seem to work:

        this.props.navigation.navigate(
            NavigationActions.reset({
                index: 0,
                actions: [
                    NavigationActions.navigate({
                        routeName: 'Home',
                    }),
                    NavigationActions.navigate({
                        routeName: 'Items',
                        params: {
                            ...
                        },
                    }),
                ],
            })
        )

@markflorisson
Copy link
Author

markflorisson commented Feb 3, 2017

Or do we have to nest the second 'navigate' action inside the first one, like so?

        this.props.navigation.navigate(
            NavigationActions.reset({
                index: 0,
                actions: [
                    NavigationActions.navigate({
                        routeName: 'Home',
                        actions: [
                            NavigationActions.navigate({
                                routeName: 'Items',
                                params: {
                                    ...
                                },
                            }),
                        ]
                    }),
                ],
            })
        )

@markflorisson
Copy link
Author

BTW the most reliable way to work around is it to navigate to the 'Home' screen with a param, and then to do a 'navigate' from the Home screen to whereever you need to redirect. But it's a hassle and it involves an unnecessary rendering of the home screen.

@ericvicenti
Copy link
Contributor

@markflorisson, it can be hard to see what is going wrong exactly. Can you write a test for StackRouter.getStateForAction, and verify that the reset action works correctly? The failing test will make the bug super clear.

@Paul-Todd
Copy link

@markflorisson - do you have an example of navigation with the param? I am stuck in the way at the moment

@markflorisson
Copy link
Author

@Paul-Todd hi Paul, this is the code I use in my Home screen:

class HomeScreen extends Component {
    redirectKey = null

    render = () => {
        const { redirectRoute, redirectKey, redirectParams } = this.params
        if (redirectRoute && redirectKey != this.redirectKey) {
            this.redirectKey = redirectKey
            setTimeout(() => {
                this.props.navigation.navigate(redirectRoute, redirectParams)
            }, 0)
        }

        return (
            <HomeView
                navigation={this.props.navigation}
                />
        )
    }
}

Putting the redirect logic in componentDidMount doesn't work, as componentDidMount is only called once on the home screen (not sure when it is unmounted).

From another page I then reset to the home screen as follows:

import { NavigationActions } from 'react-navigation'
import shortid from 'shortid'

const resetRoute = (navigation, params) => {
    navigation.dispatch(
        NavigationActions.reset({
            index: 0,
            actions: [
                NavigationActions.navigate({
                    routeName: 'Home',
                    params: params,
                }),
            ],
        }),
    )
}

class SomeOtherScreen extends Component {
    handlePress = () => {
        resetRoute(this.props.navigation, {
            redirectRoute: 'ScreenToRedirectTo',
            redirectKey: shortid(),
            redirectParams: {
                ... params for ScreenToRedirectTo ...
            },
        })
    }

    ...
}

NOTE: the shortid() is necessary in order to only perform the redirect once (e.g. if the user pressed "back" to go back to the home screen we no longer wish to perform a redirect).

Disclaimer: This code is pretty terrible, I don't recommend using it if you can avoid it :)

@markflorisson
Copy link
Author

@ericvicenti Will give it a go, thanks!

@markflorisson
Copy link
Author

Ok so the issue is not necessarily with the reducers so much as how to use the reset action. What you need to do is make sure you set the correct index. This works for me:

        this.props.navigation.dispatch(
            NavigationActions.reset({
                index: 1,
                actions: [
                    NavigationActions.navigate({
                        routeName: 'Home',
                    }),
                    NavigationActions.navigate({
                        routeName: 'Bar',
                        params: {
                            ...,
                        },
                    }),
                ],
            }),
        )

Setting the 'index' as part of the 'navigate' doesn't seem to work.

@markflorisson
Copy link
Author

(BTW: What is the reason for 'index'? Can the reducer not just take 'actions.length'?)

@ericvicenti
Copy link
Contributor

Theoretically, I think index should be optional and would default to actions.length, but we don't have that feature right now. PR would be accepted for it.

Closing this out because it seems like everything is working. And I'll continue to think about how the API could be improved.

@davebro
Copy link

davebro commented Oct 15, 2017

@CoteViande This solved ALL my navigation problems!

ManuelKu1223 pushed a commit to ManuelKu1223/react-navigation that referenced this issue Jun 5, 2019
- Added the index
- Actions is now an array
Related to react-navigation/react-navigation#59 (comment)
satya164 pushed a commit that referenced this issue Feb 7, 2020
By shipping source files and setting the ‘react-native’ entry point to src, the RN packager can parse the module and provide source maps when debugging from your app. This makes the install slightly heavier but is worth it for the improved ergonomics.
sourcecode911 pushed a commit to sourcecode911/react-navigation that referenced this issue Mar 9, 2020
- Added the index
- Actions is now an array
Related to react-navigation#59 (comment)
ken0nek pushed a commit to ken0nek/react-navigation that referenced this issue Dec 19, 2022
By shipping source files and setting the ‘react-native’ entry point to src, the RN packager can parse the module and provide source maps when debugging from your app. This makes the install slightly heavier but is worth it for the improved ergonomics.
ShyJuno pushed a commit to ShyJuno/custom-react-navigation that referenced this issue Apr 18, 2024
- Added the index
- Actions is now an array
Related to react-navigation/react-navigation#59 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests