Gray
Gray

Reputation: 579

how to navigate to specific screen after pressing rnfirebase notification

i'm using react native firebase and i'm getting notification whenever is needed, and these notifications have some data to navigate to specific screen. i used the firebase documentation to implement the functionality but it's not working as it's supposed to

Here is the document i've used Firebase & React-Navigation and my code looks something like this :

const Stack = createStackNavigator();
const Router = () => {
    const navigation = useNavigation();
    const [loading, setLoading] = useState(true);
    const [initialRoute, setInitialRoute] = useState('Splash');

useEffect(() => {
    //fcm
    registerAppWithFCM();
    // checkRNFBPermission();

    const unsubscribe = messaging().onMessage(async remoteMessage => {
        console.log('remote DATAAAAAAAAAAAAAAAAAAAAAAAA : ',remoteMessage.data);
        // switch (remoteMessage.data.screen) {
        //     case 'answer':{
        //         console.log('inside switch condition 1 !!!!!!!!!!!!!');
        //         useNavigation().navigate('Profile');
        //         break;
        //     }
        //     case 'AnswerQuestion':{
        //         console.log('inside switch condition 2 !!!!!!!!!!!!!');
        //         useNavigation().navigate('Profile');
        //         break;
        //     }

        //     default:
        //         break;
        // }
        // Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
        // const owner = JSON.parse(remoteMessage.data.owner);
        // const user = JSON.parse(remoteMessage.data.user);
        // const picture = JSON.parse(remoteMessage.data.picture);
    });

    // Assume a message-notification contains a "type" property in the data payload of the screen to open
   messaging().onNotificationOpenedApp(remoteMessage => {
      console.log(
        'Notification caused app to open from background state:',
        remoteMessage.notification,
      );
      navigation.navigate('Profile');

    });
    //  Check whether an initial notification is available
    messaging()
    .getInitialNotification()
    .then(remoteMessage => {
      if (remoteMessage) {
        console.log(
          'Notification caused app to open from quit state:',
          remoteMessage.data, //notification
        );
      }
      setLoading(false);
    });

    messaging().setBackgroundMessageHandler(async remoteMessage => {
        console.log('Message handled in the background!', remoteMessage);
    });

    return unsubscribe;
    //fcm
}, []);

//fcm
checkRNFBPermission = async() => {
    const enabled = await messaging().hasPermission();
    if(enabled){
        messaging()
        .getToken()
        .then(token => {
            // console.log('deviceeeee fcm token ------> ', token);
        });    
    }else{
        requestUserPermission();
    }
}
registerAppWithFCM = async() => {
    await messaging().registerDeviceForRemoteMessages();
}
requestUserPermission = async() =>  {
    const settings = await messaging().requestPermission();
    if (settings) {
        console.log('Permission settings:', settings);
    }
}
//fcm

renderLoading = () => (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'  }}>
        <Text>Domanda</Text>
        <ActivityIndicator size='large' color={colors.darkerTeal} />
    </View>
);

//firebase
if (loading) {
    return null;
}
//firebase
return(
    <Provider store={store}>
        <PersistGate persistor={persistor} loading={this.renderLoading()}>
            <Root>
                <NavigationContainer>
                    <Stack.Navigator initialRouteName={initialRoute} headerMode="none">
                        <Stack.Screen name="Splash" component={Splash} />
                        <Stack.Screen name="Login" component={Login} />
                        <Stack.Screen name="Main" component={Main} />
                        <Stack.Screen name="AppIntro" component={AppIntro} />
                        <Stack.Screen name="Tags" component={Tags} />
                        <Stack.Screen name="Answers" component={Answers} />
                        <Stack.Screen name="Profile" component={Profile} />
                        <Stack.Screen name="EditInfo" component={EditInfo} />
                        <Stack.Screen name="ChangePassword" component={ChangePassword} />
                        <Stack.Screen name="AnswerQuestion" component={AnswerQuestion} />
                        <Stack.Screen name="ContactUs" component={ContactUs} />
                    </Stack.Navigator>
                </NavigationContainer>
            </Root>
        </PersistGate>
    </Provider>
)

};

export default Router;

but when i add usenavigation and i want to use it it throws this error: Error: We couldn't find a navigation object. Is your component inside a screen in a navigator?

enter image description here

i can not use navigation.navigate('Profile'); to navigate to a specific screen.

Upvotes: 2

Views: 7903

Answers (2)

Alex Aymkin
Alex Aymkin

Reputation: 518

Was struggling 4 hours...

  1. Some were in component, were navigation is available (in my case "Home screen")

    // last import

     import { ScrollWrapper } from './styles'
    
     export const navigationRef = React.createRef();
         export const isReadyRef = React.createRef();
         export function navigate(name, params) {
           if (isReadyRef.current && navigationRef.current) {
             // Perform navigation if the app has mounted
             navigationRef.current.navigate(name, params);
           } else {
             console.log(' else [ELSE] --- ')
             // You can decide what to do if the app hasn't mounted
             // You can ignore this, or add these actions to a queue you can call later
           }
         }
    
     // component start
     export const SocialHomeScreen = () => {...
    
  2. In App.js

import { navigate, navigationRef, isReadyRef } from './screens/PeopleAroundYou/index'

// .... navigators

const App = () => {
  const [isAuth, setIsAuth] = useState(false)

  AsyncStorage.getItem('pushNotify').then(value => {
    console.log('value --- ', value)
    console.log('JSON.parse(value) --- ', JSON.parse(value))
  }).catch(error => {
    console.log('error --- ', error)
  })

  // foreground message arrived
  useEffect(() => {
    return messaging().onMessage(async remoteMessage => {
      const { data, notification } = remoteMessage
      if (data.type === 'activity-check-in') {

        console.log(' A new FCM message arrived! --- ')
        console.log('data --- ', data)
        console.log('notification --- ', notification)
        console.log(' navigator --- ',  navigate)
        console.log('navigationRef.current.getRootState() --- ', navigationRef.current.getRootState())
        

        switch (data.category) {
          case 'fitness':
            // navigate to nested screen
            navigate(routes.Fitness, {
              screen: routes.ActivityDetails,
              params: { activityId: data.eventId}
            })
            break
          case 'companionship':
            navigate(routes.Companionships, {
              screen: routes.ActivityDetails,
              params: { activityId: data.eventId}
            })
            break
          case 'volunteering':
            navigate(routes.Volunteering, {
              screen: routes.ActivityDetails,
              params: { activityId: data.eventId}
            })
            break
          case 'wellbeing':
            navigate(routes.Wellbeing, {
              screen: routes.ActivityDetails,
              params: { activityId: data.eventId}
            })
            break
          
        }

      }
    })
  }, [])


  useEffect(() => {
    SplashScreen.hide()

    fcmService.registerAppWithFCM()
    fcmService.register(onRegister, onNotification, onOpenNotification)
    localNotificationService.configure(onOpenNotification)

    function onRegister(token) {
      console.log('[App] onRegister: ', token)
    }

    function onNotification(notify) {
      console.log('[App] onNotification: ', notify)
      const options = {
        soundName: 'default',
        playSound: true, //,
        // largeIcon: 'ic_launcher', // add icon large for Android (Link: app/src/main/mipmap)
        // smallIcon: 'ic_launcher' // add icon small for Android (Link: app/src/main/mipmap)
      }
      localNotificationService.showNotification(
        0,
        notify.title,
        notify.body,
        notify,
        options,
      )
    }

    function onOpenNotification(notify) {
      console.log('[App] onOpenNotification: ', notify)
      Alert.alert('Open Notification: ' + notify.body)
    }

    return () => {
      console.log('[App] unRegister')
      fcmService.unRegister()
      localNotificationService.unregister()
    }
  }, [])

  const authContext = useMemo(() => {
    return {
      login: () => {
        setIsAuth(true)
      },
      logout: () => {
        setIsAuth(false)
      },
    }
  })

  return (
    <AuthContext.Provider value={authContext}>
      <ThemeProvider theme={theme}>
        <NavigationContainer
          ref={navigationRef}
          onReady={() => {
            isReadyRef.current = true
          }}
          linking={linking}
          fallback={
            <View style={{ justifyContent: 'center', alignItems: 'center' }}>
              <Loader loading size='large' color='#61A5C8'/>
            </View>
          }
        >
          {isAuth ? <AuthorizedTabs /> : <NonAuthorizedStack/>}
        </NavigationContainer>
      </ThemeProvider>
    </AuthContext.Provider>
  )
}

Upvotes: 1

Stevetro
Stevetro

Reputation: 1963

You're receiving the message in App.js whats outside of your StackNavigator. You can use a ref to use the navigation property of the navigator

define the navigator in the top of you app.js

var navigator = null;

then add an ref to the navigator

<Stack.Navigator 
  initialRouteName={initialRoute}
  headerMode="none"
   ref={nav => {
     navigator = nav;
   }}
 >

and push your route inside the receive method

navigator.dispatch(
  NavigationActions.navigate({
     routeName: 'theRoute',
       params: {},
   }),
 );

Upvotes: 1

Related Questions