import { CommonActions } from '@react-navigation/routers'; import type { NavigationContainerEventMap, NavigationContainerRef, NavigationContainerRefWithCurrent, } from './types'; export const NOT_INITIALIZED_ERROR = "The 'navigation' object hasn't been initialized yet. This might happen if you don't have a navigator mounted, or if the navigator hasn't finished mounting. See https://reactnavigation.org/docs/navigating-without-navigation-prop#handling-initialization for more details."; export default function createNavigationContainerRef< ParamList extends {} = ReactNavigation.RootParamList >(): NavigationContainerRefWithCurrent { const methods = [ ...Object.keys(CommonActions), 'addListener', 'removeListener', 'resetRoot', 'dispatch', 'isFocused', 'canGoBack', 'getRootState', 'getState', 'getParent', 'getCurrentRoute', 'getCurrentOptions', ] as const; const listeners: Record void)[]> = {}; const removeListener = ( event: string, callback: (...args: any[]) => void ) => { if (listeners[event]) { listeners[event] = listeners[event].filter((cb) => cb !== callback); } }; let current: NavigationContainerRef | null = null; const ref: NavigationContainerRefWithCurrent = { get current() { return current; }, set current(value: NavigationContainerRef | null) { current = value; if (value != null) { Object.entries(listeners).forEach(([event, callbacks]) => { callbacks.forEach((callback) => { value.addListener( event as keyof NavigationContainerEventMap, callback ); }); }); } }, isReady: () => { if (current == null) { return false; } return current.isReady(); }, ...methods.reduce((acc, name) => { acc[name] = (...args: any[]) => { if (current == null) { switch (name) { case 'addListener': { const [event, callback] = args; listeners[event] = listeners[event] || []; listeners[event].push(callback); return () => removeListener(event, callback); } case 'removeListener': { const [event, callback] = args; removeListener(event, callback); break; } default: console.error(NOT_INITIALIZED_ERROR); } } else { // @ts-expect-error: this is ok return current[name](...args); } }; return acc; }, {}), }; return ref; }