From b6accd03f69dd438e595094d8bf8599cc12e71ac Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 12 Feb 2020 20:59:58 +0100 Subject: [PATCH] fix: throw a descriptive error if navigation object hasn't initialized --- packages/core/src/BaseNavigationContainer.tsx | 11 +++++ .../BaseNavigationContainer.test.tsx | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/packages/core/src/BaseNavigationContainer.tsx b/packages/core/src/BaseNavigationContainer.tsx index b261b753..ac8215cd 100644 --- a/packages/core/src/BaseNavigationContainer.tsx +++ b/packages/core/src/BaseNavigationContainer.tsx @@ -22,6 +22,9 @@ type State = NavigationState | PartialState | undefined; const MISSING_CONTEXT_ERROR = "We couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'? See https://reactnavigation.org/docs/en/getting-started.html for setup instructions."; +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. You can ensure that all navigators have mounted after the callback to 'useEffect' is called in your root component."; + export const NavigationStateContext = React.createContext<{ isDefault?: true; state?: NavigationState | PartialState; @@ -195,10 +198,18 @@ const BaseNavigationContainer = React.forwardRef( const dispatch = ( action: NavigationAction | ((state: NavigationState) => NavigationAction) ) => { + if (listeners[0] == null) { + throw new Error(NOT_INITIALIZED_ERROR); + } + listeners[0](navigation => navigation.dispatch(action)); }; const canGoBack = () => { + if (listeners[0] == null) { + throw new Error(NOT_INITIALIZED_ERROR); + } + const { result, handled } = listeners[0](navigation => navigation.canGoBack() ); diff --git a/packages/core/src/__tests__/BaseNavigationContainer.test.tsx b/packages/core/src/__tests__/BaseNavigationContainer.test.tsx index ee9b08f3..bfcb14fe 100644 --- a/packages/core/src/__tests__/BaseNavigationContainer.test.tsx +++ b/packages/core/src/__tests__/BaseNavigationContainer.test.tsx @@ -501,3 +501,51 @@ it('emits state events when the state changes', () => { ], }); }); + +it('throws if there is no navigator rendered', () => { + expect.assertions(1); + + const ref = React.createRef(); + + const element = ; + + render(element); + + act(() => { + expect(() => ref.current?.dispatch({ type: 'WHATEVER' })).toThrow( + "The 'navigation' object hasn't been initialized yet." + ); + }); +}); + +it("throws if the ref hasn't finished initializing", () => { + expect.assertions(1); + + const ref = React.createRef(); + + const TestNavigator = (props: any) => { + const { state, descriptors } = useNavigationBuilder(MockRouter, props); + + return descriptors[state.routes[state.index].key].render(); + }; + + const TestScreen = () => { + React.useEffect(() => { + expect(() => ref.current?.dispatch({ type: 'WHATEVER' })).toThrow( + "The 'navigation' object hasn't been initialized yet." + ); + }, []); + + return null; + }; + + const element = ( + + + + + + ); + + render(element); +});