javascriptreact-nativeexporeact-navigationexpo-notifications

Navigate into nested navigator when Expo Push Noti is clicked


I am trying to navigate to a certain screen whenever I click on an Expo Push Notification. The screen that I want to navigate to is rather deep into the NavigationContainer.

However, the issue that I am facing now is being unable to even navigate to anywhere except having the app restart on its own. I'm running all the testing on a real device.

I'm using Expo to work on this school project.

I have only managed to find this question in SO and Expo Forums (duplicate) useful.

This is my application Navigation structure:

-Navigation Structure-

AppNavigator
  DrawerNavigator
    MainNavigator
      TabsNavigator
      StackNavigator
      StackNavigator
        TabsNavigator
          ScreenA (Want to navigate to)
          ScreenB (Want to navigate to)
        StackNavigator
          ScreenA
          ScreenB
      StackNavigator
    ScreenA
  AuthNavigator
  RegisterNavigator
  ScreenA

There is a useNotifications hook created and I called it in the main App Navigator where the NavigationContainer resides in.

import React, { useEffect } from 'react';
import * as Notifications from 'expo-notifications';

import navigation from '../navigation/RootNavigation';

const useNotifications = () => {
  const notiResponseListener = React.createRef();

  useEffect(() => {
    notiResponseListener.current =
      Notifications.addNotificationResponseReceivedListener(res => {
        console.log(res.notification.request.content.data);
        console.log('addNotificationResponseReceivedListener');

        navigation.navigate(
          ('DrawerNavigator', { screen: 'ChangePassword' }),
          {}
        );
      });

    return () =>
      Notifications.removeNotificationSubscription(notiResponseListener);
  }, []);
};

export default useNotifications;

There is a ref added to the NavigationContainer.

import { navigationRef } from '../navigation/RootNavigation';
import useNotifications from '../hooks/useNotifications';

const App = createStackNavigator();

const AppNavigator = () => {
  useNotifications();

  return (
    <NavigationContainer ref={navigationRef}>
      <App.Navigator headerMode='none'>
        ...
      </App.Navigator>
    </NavigationContainer>
  );
};

And lastly, the file that contains the ref used in the NavigationContainer.

import React from 'react';

export const navigationRef = React.createRef();

const navigate = (name, params) => {
  console.log('entered navigating'); // does not print
  navigationRef.current?.navigate(name, params);
};

export default {
  navigate
};

I have searced high and low but I can't seem to find out what's wrong. Looked at the documentation for Expo and React Navigation but I'm not sure what's going on. It's my first time working on Push Notifications and such a case.

I appreciate any help, thank you


Solution

  • We have fixed the problem with the usage of useLastNotificationResponse.

        const [notification, setNotification] = useState(false);
        const notificationListener = useRef();
        const responseListener = useRef();
    
        //add this
        const lastNotificationResponse =
            Notifications.useLastNotificationResponse();
    
        useEffect(() => {
            if (lastNotificationResponse) {
                //console.log(lastNotificationResponse);
    
                //get the route
                const route = JSON.stringify(
                    lastNotificationResponse.notification.request.content.data.route
                );
    
                //use some function to return the correct screen by route
                getFullPath(JSON.parse(route));
            }
        }, [lastNotificationResponse]);
    

    Based on your routes, navigate to correct screen getFullPath:

    import { navigationRef } from "./rootNavigation";
    import routes from "./routes";
    
    export function getFullPath(route) {
        switch (route) {
            case "HomeScreen":
                return navigationRef.current?.navigate(routes.HOME);
            case "Account":
                return navigationRef.current?.navigate(routes.ACCOUNT, {
                    screen: routes.ACCOUNTSCREEN,
                });
            default:
                return;
        }
    }