By hellofanengineer


2017-12-21 17:35:09 8 Comments

Here is my project file hierarchy

RootTabNavigator
    | AuthStackNavigator // I want to go back to this navigator
          | AuthoScreen
    | Welcome Screen
    | MainTabNavigator // I want to reset MainTabNavigator 
          | FeedStacknavigator
                   | Screen A
          | OtherStackNavigatorOne
                   | Screen E
          | OtherStackNavigatorTwo
                   | Screen D
          | MenuStackNavigator 
                   | Menuo <-I'm here and want to reset to 'MainTabNavigator' 
                             and go BACK to 'AuthScreen'
           | Screen B
                   | Screen C

Problem

The user is on Menuo Screen under MenuStackNavigator and MainTabNavigator.

If user doesn't have a token (when user logs out), user goes back to the Auth Screen.

But at the same time I want to RESET MainTabNavigator. You can unmount , perform NavigationActions.init() or whatever you can. I prefer NavigationActions.init()

I just want to set MainTabNavigator to its very first time.

Code

if there is no token, I go back to Auth Screen (This is working)

This code if the part of Menuo Screen

componentWillReceiveProps(nextProps) {
    if ( nextProps.token == undefined || _.isNil(nextProps.token) ) {
      const backAction = NavigationActions.back({
        key: null
      })
      nextProps.navigation.dispatch(backAction);
      ...

(Question) How can we reset MainTabNavigator including child StackNavigators?

MainTabNavigator.js

export default TabNavigator(
    {
        Feed: {
          screen: FeedStacknavigator,
        },
        OtherOne: {
          screen: OtherStackNavigatorOne,
        }
        ...
    }, {
        navigationOptions: ({navigation}) => ){
            header: null,
        tabBarIcon: ({focused}) => ...
        ...
    }

Possible Solution

I can maybe change MainTabNavigator from function to class and deal with resetting TabNavigator there. (I'm not sure).

This time, I need a concrete working example. I've been reading doc and applying to my app but I couldn't solve this.

Please let me know if anything is unclear.

UPDATE

const RootTabNavigator = TabNavigator ({
    Auth: {
      screen: AuthStackNavigator,
    },
    Welcome: {
      screen: WelcomeScreen,
    },
    Main: {
      screen: MainTabNavigator,
    },
  }, {
    navigationOptions: () => ({
     ...
  }
);

export default class RootNavigator extends React.Component {
  componentDidMount() {
    this._notificationSubscription = this._registerForPushNotifications();
  }

3 comments

@idancali 2017-12-26 17:30:17

What you need is to initialize the navigator to the initial state. You can do that with NavigationActions.init(). You can learn more about Navigations Actions here.

You can do this by creating a Custom Navigation Action, read more about them here.

Here's some code that would do that for you:

// First get a hold of your navigator
const navigator = ...

// Get the original handler
const defaultGetStateForAction = navigator.router.getStateForAction

// Then hook into the router handler
navigator.router.getStateForAction = (action, state) => {

  if (action.type === 'MyCompleteReset') {
     // For your custom action, reset it all
     return defaultGetStateForAction(NavigationActions.init())
  }

  // Handle all other actions with the default handler
  return defaultGetStateForAction(action, state)
}

In order to trigger your Custom Navigation Action, you have to dispatch it as follows from within your React Component:

  this.props.navigation.dispatch({
      type: "MyCompleteReset",
      index: 0
    })

@hellofanengineer 2018-01-02 10:06:36

Um.. how do we trigger 'MyCompleteReset' action type??? Sorry for the late reply

@hellofanengineer 2018-01-02 10:14:54

stackoverflow.com/questions/48059045/… <- Here is continuous problem

@hellofanengineer 2018-01-02 14:45:50

I don't undetstand how to apply to my code. :(

@hellofanengineer 2018-01-02 14:55:46

How do we pass action.type

@hellofanengineer 2018-01-03 07:30:46

I need concrete working example. I hope the bounty+200 helps

@idancali 2018-01-06 08:00:36

Hey @JohnBaek check out the edited answer. I added an example of how to trigger your custom action.

@zarcode 2018-01-03 11:14:07

This should work in most cases:

componentWillReceiveProps(nextProps) {
    if ( nextProps.token == undefined || _.isNil(nextProps.token) ) {

        let action = NavigationActions.reset({
            index: 0,
            key: null,
            actions: [
                NavigationActions.navigate({routeName: 'Auth'})
            ]
        });

        nextProps.navigation.dispatch(action);
    }
    ...
}

Or try by enhancing your navigator with custom action:

const changeAppNavigator = Navigator => {
   const router = Navigator.router;

   const defaultGetStateForAction = router.getStateForAction;

   router.getStateForAction = (action, state) => {
       if (state && action.type === "RESET_TO_AUTH") {
          let payLoad = {
              index: 0,
              key: null,
              actions: [NavigationActions.navigate({routeName: "AuthStackNavigator"})]
          };

          return defaultGetStateForAction(NavigationActions.reset(payLoad), state);
          // or this might work for you, not sure:
          // return defaultGetStateForAction(NavigationActions.init(), state)
       }
       return defaultGetStateForAction(action, state);
  };

  return Navigator;
};

const screens = { ... }

RootTabNavigator = changeAppNavigator(TabNavigator(screens, {
  initialRouteName: ...,
  ...
}));

Then in your Menuo Screen do:

componentWillReceiveProps(nextProps) {
    if ( nextProps.token == undefined || _.isNil(nextProps.token) ) {

        nextProps.navigation.dispatch({type: "RESET_TO_AUTH"});
    ...

@hellofanengineer 2018-01-03 11:20:01

Oh the code is easy to read and I understand how it might work!! But my RootTabNavigator is already defined as class. Can we use same logic for my RootTabNavigator? Thanks!

@zarcode 2018-01-03 11:56:20

@JohnBaek you should be able to use it on any navigator, I have tried it in few cases, worked for me, have it in my projects...

@hellofanengineer 2018-01-04 22:41:40

perfect. It worked. Simple and easy :)

@hellofanengineer 2018-01-05 05:02:48

I'm getting undefined is not an object (evaluating '_this3._component.setNativeProps') when I try to dispatch({type: "RESET_TO_AUTH"}) on other screen(MenuScreen > ChangePasswordScreen)..

@zarcode 2018-01-05 06:57:53

Before doing this.props.navigation.dispatch({type: "RESET_TO_AUTH"}); can you please confirm that navigation is present on props?

@hellofanengineer 2018-01-05 07:00:47

Sure. it prints Object { "dispatch": [Function anonymous], "goBack": [Function goBack], "navigate": [Function navigate], "setParams": [Function setParams], "state": Object { "key": "id-xxxxx578521-6", "params": undefined, "routeName": "ChangePassword", }, }

@hellofanengineer 2018-01-05 07:26:22

But the weird thing is that it doesn't go back to Auth Screen. It goes to MainTabNavigator. This is weird.

@hellofanengineer 2018-01-05 07:43:45

This is very weird. I added redux props changed then pass it to componenetWillReceiveProps then I dispatched RESET_TO_AUTH. Then same error.

@hellofanengineer 2018-01-05 07:44:23

printed nextProps.navigation and it prints 'Menuo' Screen. :( Why isn't it woking correctly this is so annoying

@hellofanengineer 2018-01-05 07:52:04

@hellofanengineer 2018-01-05 07:54:48

yeah it's same thing. and MenuScreen is a parent of ChangePasswordScreen in same navigator. And I don't see your point.

@hellofanengineer 2018-01-05 07:55:16

can we talk in this question? stackoverflow.com/questions/48108205/…

@hellofanengineer 2018-01-05 08:04:55

@clickclickonsal 2018-01-03 22:50:37

You can define custom navigation logic by extending the router. To accomplish what you want that you described in the project file hierarchy in your question you can do something like this below.

MainTabNavigator.js

...

RootTabNavigator.router.getStateForAction = (action, state) => {
  if (state && action.type === 'GoToAuthScreen') {
    return {
      ...state,
      index: 0,
    };
  }

  return RootTabNavigator.router.getStateForAction(action, state);
};

MainTabNavigator.router.getStateForAction = (action, state) => {
  if (state && action.type === 'GoToAuthScreen') {
    return {
      ...state,
      index: 0,
    };
  }

  return MainTabNavigator.router.getStateForAction(action, state);
};

MenuStackNavigator.router.getStateForAction = (action, state) => {
  if (state && action.type === 'GoToAuthScreen') {
    return {
      ...state,
      index: 0,
    };
  }

  return MenuStackNavigator.router.getStateForAction(action, state);
};

In the Menuo Screen file

componentWillReceiveProps(nextProps) {
  if ( nextProps.token == undefined || _.isNil(nextProps.token) ) {
    const goToAuthScreen = () => ({
      type: 'GoToAuthScreen',
    });

    nextProps.navigation.dispatch(goToAuthScreen);
    ...
  }
}

Related Questions

Sponsored Content

1 Answered Questions

[SOLVED] Error using StackNavigator with switching screens

1 Answered Questions

1 Answered Questions

2 Answered Questions

How to push a new scene using react-navigation?

2 Answered Questions

[SOLVED] How to go back to parent screen in react-navigation?

Sponsored Content