React Navigation 2: How to check previous scene and to disable tab change

Viewed 2640

I have a tab navigation. One of my tabs has a form and I would like to disable navigate event if my form data is not saved.

In ver.1, the tabBarOnPress method provides previousScene, scene and jumpToIndex, so I was able to check which scene I am leaving and to access its props.

Now in ver.2, the tabBarOnPress method provides the navigation props for the scene, but the previous scene prop is missing :/

navigationOptions: {
    tabBarOnPress: ({ navigation, defaultHandler }) => {
        // Check the previous screen
        // If I am leaving the home screen and the user has unsaved data
        // disable tab navigation
        // else change to the pressed tab
    },
},

Also, I tried with the navigation event listeners but the NAVIGATE action is already dispatched:

props.navigation.addListener('willBlur', () => {
    // Disable tab switching;
}),

Simple snack: https://snack.expo.io/@hristoeftimov/handle-tab-changes-in-react-navigation-v2

Any solutions how to disable tab switching before leave a tab?

2 Answers

I have found a much simpler way, using the getStateForAction.

const defaultGetStateForAction = MainStack.router.getStateForAction;
MainStack.router.getStateForAction = (action, state) => {
    if (!state) {
        return defaultGetStateForAction(action, state);
    }

    if (
        action.type === NavigationActions.NAVIGATE
        && state.routes[state.index].key === 'HomeTab'
    ) {
        const tab = state.routes[state.index];
        const currentRoute = tab.routes[tab.index];
        const currentRouteParams = currentRoute.params;

        if (currentRouteParams && currentRouteParams.isNavigationDisabled) {
            return currentRouteParams.showConfirmationDialog(action);
        }
    }

    return defaultGetStateForAction(action, state);
}

Every time when I switch between the tabs it jumps into getStateForAction where I can access the leaving tab (from state) and the next screen (from action).

So, when my action is NAVIGATE and the leaving screen/route is HoneTab I can change/disable the default state for action and to trigger showConfirmationDialog() - This is a function that I can set as a route parameter to my HomeTab screen.

The navigation object contains the data you need, as it holds the navigation state prior to the navigation to the new tab. This navigation state has both the screen from which you're navigating and its params.

In order to get the state you can use the following function:

function getCurrentRoute(navState) {
  if (!navState) {
    return null;
  }
  const route = navState.routes[navState.index];

  if (route.routes) {
    return getCurrentRoute(route); // nested routes
  } else {
    return {
      name: route.routeName,
      params: { ...route.params }
    };
  }
}

So now you can use this function inside the onPress handler. Something like this:

navigationOptions: {
    tabBarOnPress: ({ navigation, defaultHandler }) => {
        const currentRoute = getCurrentRoute(navigation.state);
        if (currentRoute.name !== 'Home' || !currentRoute.params.isNavigationDisabled) {
            defaultHandler();
        }
    }
}

Of course this means that you'll need to manage a navigation param called isNavigationDisabled in your Home screen by using this.props.navigation.setParams method.

Also, I hope I was correct with the screen name, if not just debug it.

Related