Navigating without the navigation prop - typescript typings
Asked Answered
C

5

5

I'm trying to include React Navigation 6.x into a React Native project with Redux and therefore need to be able to access the navigator from outside components.

I'm following this guide (Navigating without the navigation prop) and have essentially the same code as in their example, which functionally works fine:

import { createNavigationContainerRef } from '@react-navigation/native';

export const navigationRef = createNavigationContainerRef()

export function navigate(name, params) {
  if (navigationRef.isReady()) {
    navigationRef.navigate(name, params);
  }
}

However I'm also using Typescript.

React Navigation also have a guide on integrating Typescript (Type checking with TypeScript) which shows how to type the navigation ref itself, which works also fine:

export const navigationRef = createNavigationContainerRef<RootStackParamList>();

There is no example for typing the navigate function though, and I haven't been able to get anything to work.

I thought the solution would be to copy the typing for the navigationRef.navigate() method (defined here) and simply apply it to the wrapper function:

// navigationRef.navigate() typing...
//
// navigate<RouteName extends keyof ParamList>(
//   ...args: undefined extends ParamList[RouteName]
//     ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
//     : [screen: RouteName, params: ParamList[RouteName]]
// ): void;


type ParamList = RootStackParamList;
type Navigate = <RouteName extends keyof ParamList>(
  ...args: undefined extends ParamList[RouteName]
    ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
    : [screen: RouteName, params: ParamList[RouteName]]
) => void;

export const navigate: Navigate = (name, params) => {
  if (navigationRef.isReady()) {
    navigationRef.navigate(name, params);
  }
}

// or...

export const navigate: typeof navigationRef.navigate = (name, params) => {
  if (navigationRef.isReady()) {
    navigationRef.navigate(name, params);
  }
}

That unfortunately gives the following error:

Argument of type '[(undefined extends RootStackParamList[RouteName] ? [screen: RouteName] | [screen: RouteName, params: RootStackParamList[RouteName]] : [screen: ...])[0], (undefined extends RootStackParamList[RouteName] ? [screen: ...] | [screen: ...] : [screen: ...])[1]]' is not assignable to parameter of type 'undefined extends RootStackParamList[(undefined extends RootStackParamList[RouteName] ? [screen: RouteName] | [screen: RouteName, params: RootStackParamList[RouteName]] : [screen: ...])[0]] ? [screen: ...] | [screen: ...] : [screen: ...]'.ts(2345)
Cage answered 11/2, 2022 at 14:16 Comment(0)
M
8

Instead of navigationRef.navigate(name, params), you can try with this

import { CommonActions } from '@react-navigation/native';

export function navigateTo(routeName: string, params?: object) {
  if (navigationRef.isReady()) {
    navigationRef.dispatch(CommonActions.navigate(routeName, params));
  }
}

this works for me

Munro answered 4/10, 2022 at 5:28 Comment(0)
C
1

This worked for me

export const navigate = (name: keyof RootStackParamList) => {
  if (navigationRef.isReady()) {
    navigationRef.navigate(name)
  }
}

even better:

export const navigate = (
  name: keyof RootStackParamList,
  params?: StackScreenProps<RootStackParamList>['route']['params']
) => {
  if (navigationRef.isReady()) {
    navigationRef.navigate(name, params)
  }
}
Cromorne answered 23/5, 2022 at 14:45 Comment(2)
how to import StackScreenProps?Cimmerian
import { StackScreenProps } from '@react-navigation/stack'Cromorne
W
0

You can reuse the type of navigationRef.navigate

export const navigate: typeof navigationRef.navigate = (name, params) => {
  if (navigationRef.isReady()) {
    navigationRef.navigate(name, params);
  }
}
Wacker answered 11/2, 2022 at 15:36 Comment(4)
Thanks that's definitely more succinct, but I get the same error unfortunately.Cage
If you get the same error then your annotation for createNavigationContainerRef isn't correct. You need to share the type of RootStackParamList as well as the code you're using to call navigateWacker
It doesn't seem to matter what RootStackParamList is. It can simply be interface RootStackParamList { Home: undefined }. I'm not calling it anywhere yet but that wouldn't really be relevant anyway.Cage
Any luck on it?Grimaldo
S
0

try this:

export const navigate: typeof navigationRef.navigate = (name, params?) => {
  if (navigationRef.isReady()) {
    navigationRef.navigate(name, params);
  }
};
Schwing answered 9/12, 2022 at 6:43 Comment(0)
T
0

this worked for me:

// Overload Functions for multiple arguments conditions
export function navigate<RouteName extends keyof RootStackParamList>(
  ...args: // this first condition allows us to iterate over a union type
  // This is to avoid getting a union of all the params from `RootStackParamList[RouteName]`,
  // which will get our types all mixed up if a union RouteName is passed in.
  RouteName extends unknown
    ? // This condition checks if the params are optional,
      // which means it's either undefined or a union with undefined
      undefined extends RootStackParamList[RouteName]
      ?
          | [screen: RouteName] // if the params are optional, we don't have to provide it
          | [screen: RouteName, params: RootStackParamList[RouteName]]
      : [screen: RouteName, params: RootStackParamList[RouteName]]
    : never
): void;
export function navigate<RouteName extends keyof RootStackParamList>(
  options: RouteName extends unknown
    ?
        | {
            key: string;
            params?: RootStackParamList[RouteName];
            merge?: boolean;
          }
        | {
            name: RouteName;
            key?: string;
            params: RootStackParamList[RouteName];
            merge?: boolean;
          }
    : never,
): void;

export function navigate(...args: any[]): void {
  const [screen, params] = args;
  navigationRef.navigate(screen, params);
}
Tenpin answered 30/5 at 14:54 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Par

© 2022 - 2024 — McMap. All rights reserved.