react-navigation useLinkTo outside screen but inside NavigationContainer

2.2k views Asked by At

I am surprised that useLinkTo and the like (useNavigation) can only be used inside a Navigator Screen when all they really should need is the navigation context set-up by the NavigationContainer.

Because my scenario is a notification system that wraps the whole app whose notifications should be able to link to other parts in the app.

<NavigationContainer linking={linking}>
  <Notifications>
    <RootStack/>
  </Notifications>
</NavigationContainer>

Now that it sits outside the RootStack hooks like useLinkTo, useNavigation do not work and complain about a missing context:

"Couldn't find a navigation object. Is your component inside a screen in a navigator?"

Is there a work-around for deep linking using the bare bone NavigationContainer (ref) maybe?

I tried to move the Notifications provider inside RootStack but that does not help.

1

There are 1 answers

3
itsChinVib On BEST ANSWER

In the documentation, there is a way mentioned for achieving this for useNavigation, i.e Navigating without the navigation prop.

Basically, what we do is assign the navigation container to an external ref, and then use that ref to navigate while outside the RootStack.

In file /project/Navigation/RootNavigation.js

import { createRef } from 'react';

export const navigationRef = createRef();

export function navigate(name, params) {
  navigationRef.current?.navigate(name, params);
}

export function goBack() {
  navigationRef.current?.goBack();
}

export default useRootNavigation = () => ({ navigationRef, navigate, goBack });

Edit: More functions for ref can be found here: https://reactnavigation.org/docs/navigation-container

Then in your code,

import {navigationRef} from '/project/Navigation/RootNavigation';

<NavigationContainer ref={navigationRef} linking={linking}>
  <Notifications>
    <RootStack/>
  </Notifications>
</NavigationContainer> 

Then, you can import the useRootNavigation hook in your Notification component to navigate, goback. For better explanation, you can refer https://reactnavigation.org/docs/navigating-without-navigation-prop.

However, this is not possible for useLinkTo currently I guess. I'm too searching for a way to use useLinkTo like this. If anyone finds it, please let me know!

Edit2:

For replicating the behavior of useLinkTo, I implemented the following by referring to source code for useLinkTo:

In file /project/Navigation/RootNavigation.js

import { getActionFromState, getStateFromPath } from '@react-navigation/native';

/**
* Previous code
*/

export function dispatch(action) {
  navigationRef.current?.dispatch(action);
}

export function linkTo(path, config = null) {
  var state = getStateFromPath(path, config);
  var action = getActionFromState(state);
  if (action !== undefined) {
    dispatch(action);
  }
}

export default useRootNavigation = () => ({
  navigationRef,
  navigate,
  goBack,
  dispatch,
  linkTo,
});

Then, wherever we need to use useLinkTo, outside the navigation

  const navigation = useRootNavigation(); // The hook defined in RootNavigation.js

  someFunction(()=>{
    navigation.linkTo(path, linking.config);
  })

In your(mine as well) case, we'll have to specify the linking config, or the default behavior of deep linking will be used.