skip to Main Content

I have followed the documentation of React Navigation and implemented an authentication flow as well the Linking mechanism for notifications via the linking prop of NavigationContainer.

When I open a notification when the app is running, e.g. the link https://domain/transactions/history, I am redirected to the screen TransactionHistory, and when I go back I am redirected to ChargingStations (as I want)
But, when I do the same thing from a quit state, I am being redirected as well but I can’t go back to ChargingStations and I see the warning (The action GO_BACK was not handled by any navigator)..

My code is a follow:

App.tsx

export default class App extends React.Component<Props, State> {
  public state: State;
  public props: Props;
  public centralServerProvider: CentralServerProvider;
  public deepLinkingManager: DeepLinkingManager;
  private appVersion: CheckVersionResponse;
  private readonly navigationRef: React.RefObject<NavigationContainerRef<ReactNavigation.RootParamList>>;
  private readonly appContext;
  private initialUrl: string;

  public constructor(props: Props) {
    super(props);
    this.navigationRef = React.createRef();
    this.appContext = {
      handleSignIn: () => this.setState({isSignedIn: true}),
      handleSignOut: () => this.setState({isSignedIn: false})
    };
    this.state = {
      switchTheme: false,
      navigationState: null,
      showAppUpdateDialog: false,
      isSignedIn: undefined
    };
  }

  public setState = (
    state: State | ((prevState: Readonly<State>, props: Readonly<Props>) => State | Pick<State, never>) | Pick<State, never>,
    callback?: () => void
  ) => {
    super.setState(state, callback);
  };


  public async componentDidMount() {
    // Get the central server
    this.centralServerProvider = await ProviderFactory.getProvider();

    // Setup notifications
    await Notifications.initialize();

    // Check for app updates
    this.appVersion = await Utils.checkForUpdate();
    // Set
    this.setState({
      showAppUpdateDialog: !!this.appVersion?.needsUpdate,
      isSignedIn: true
    });
  }

  public render() {
    const { switchTheme, showAppUpdateDialog, isSignedIn } = this.state;
    return switchTheme ? (
      <NativeBaseProvider>
        <GestureHandlerRootView style={{ flex: 1 }}>
          {showAppUpdateDialog && (
            <AppUpdateDialog appVersion={this.appVersion} close={() => this.setState({ showAppUpdateDialog: false })} />
          )}
          {isSignedIn == null ?
            <Loading />
            :
            this.createRootNavigator()
          }
        </GestureHandlerRootView>
      </NativeBaseProvider>
    ) : (
      <NativeBaseProvider>
        <View />
      </NativeBaseProvider>
    );
  }

  private buildLinking(): LinkingOptions<ReactNavigation.RootParamList> {
    return (
      {
        prefixes: DeepLinkingManager.getAuthorizedURLs(),
        getInitialURL: () => this.initialUrl,
        subscribe: (listener) => {
          // Listen for background notifications when the app is running,
          const removeBackgroundNotificationListener = messaging().onNotificationOpenedApp(async (remoteMessage: Notification) => {
            const canHandleNotification = await Notifications.canHandleNotificationOpenedApp(remoteMessage);
            if (canHandleNotification) {
              this.setState({isSignedIn: true}, () => listener(remoteMessage.data.deepLink));
            }
          });
          return () => {
            removeBackgroundNotificationListener();
          };
        },
        config: {
          screens: {
            AuthNavigator: {
              screens: {
                Login: 'login'
              }
            },
            AppDrawerNavigator: {
              initialRouteName: 'ChargingStationsNavigator', // <-- Initial screen I would like to always be present as first screen when navigating
              screens: {
                ChargingStationsNavigator: {
                  initialRouteName: 'ChargingStations',
                  screens: {
                    ChargingStations: 'charging-stations/all'
                  }
                },
                InvoicesNavigator: 'invoices',
                TransactionInProgressNavigator: {
                  screens: {
                    TransactionsInProgress: 'transactions/inprogress'
                  }
                },
                TransactionHistoryNavigator: {
                  screens: {
                    TransactionsHistory: 'transactions/history'
                  }
                }
              }
            }
          }
        }
      }
    );
  }

  private createRootNavigator() {
    const { isSignedIn } = this.state;
    return (
      <AuthContext.Provider value={this.appContext}>
        <SafeAreaProvider>
          <NavigationContainer
            onReady={() => this.onReady()}
            linking={this.buildLinking()}
            ref={this.navigationRef}
            onStateChange={(newState) => this.setState({navigationState: newState})}
            initialState={this.state.navigationState}
          >
            <rootStack.Navigator initialRouteName="AuthNavigator" screenOptions={{ headerShown: false }}>
              {isSignedIn ?
                <rootStack.Screen name="AppDrawerNavigator" children={createAppDrawerNavigator} />
                :
                <rootStack.Screen options={{animationTypeForReplace: 'pop'}} name="AuthNavigator" children={createAuthNavigator} />
              }
            </rootStack.Navigator>
          </NavigationContainer>
        </SafeAreaProvider>
      </AuthContext.Provider>
    );
  }
}

Expected behavior

I expect the ChargingStations screen to always be present as first screen, even from a quit state

Reproduction

https://github.com/sap-labs-france/ev-mobile/tree/upgrade_react_native

Platform

  • [X] Android
  • [X] iOS

Environment

  • [x] I’ve removed the packages that I don’t use
package version
@react-navigation/native 6.0.14
@react-navigation/drawer 6.5.1
@react-navigation/material-bottom-tabs 6.2.5
@react-navigation/stack 6.3.5
react-native-safe-area-context 4.4.1
react-native-screens 3.18.2
react-native-gesture-handler 2.8.0
react-native-reanimated 2.13.0
react-native 0.70.6
node 16.13.0
npm or yarn 9.1.2

2

Answers


  1. Chosen as BEST ANSWER

    I have managed to obtain the desired behavior by using following code in the drawer navigator props.

    backBehavior={'initialRoute'}
    

  2. You should define a nested linking route: Docs

    const config = {
      screens: {
        Home: {
          screens: {
            Profile: 'users/:id',
          },
        },
      },
    };
    
                    TransactionHistoryNavigator: {
                      screens: {
                        ChargingStationsNavigator: {
                          screens: {
                            TransactionsHistory: 'transactions/history'
                          }
                        }
                      }
                    }
    
    

    Maybe you can try to redeclare initial route (unfortunately I have currently no access to verify my suggestion):

                    TransactionHistoryNavigator: {
                      screens: {
                        TransactionsHistory: {
                         initialRouteName: 'ChargingStationsNavigator', //Define initial route here
                        }
                      }
                    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search