From 34d2fafcb74cbf4c75f142f4599d170d207cddc9 Mon Sep 17 00:00:00 2001 From: Martin Schuhfuss Date: Fri, 1 Dec 2023 16:21:36 +0100 Subject: [PATCH] fix: output an error when useMap is called outside APIProvider When used outside the APIProvider the useMap() and all dependent hooks would just silently fail and never return anything. For a better developer experience, we will now log an error to the console when the `useMap()` hook is called from a component outside the APIProvider hierarchy. --- .../__snapshots__/use-map.test.tsx.snap | 9 +++++++++ src/hooks/__tests__/use-map.test.tsx | 12 ++++++++++++ src/hooks/use-map.ts | 16 +++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/hooks/__tests__/__snapshots__/use-map.test.tsx.snap diff --git a/src/hooks/__tests__/__snapshots__/use-map.test.tsx.snap b/src/hooks/__tests__/__snapshots__/use-map.test.tsx.snap new file mode 100644 index 00000000..c12216c8 --- /dev/null +++ b/src/hooks/__tests__/__snapshots__/use-map.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it should log an error when used outside the APIProvider 1`] = ` +[ + [ + "useMap(): failed to retrieve APIProviderContext. Make sure that the component exists and that the component you are calling \`useMap()\` from is a sibling of the .", + ], +] +`; diff --git a/src/hooks/__tests__/use-map.test.tsx b/src/hooks/__tests__/use-map.test.tsx index a8c69a31..3d6a4c69 100644 --- a/src/hooks/__tests__/use-map.test.tsx +++ b/src/hooks/__tests__/use-map.test.tsx @@ -92,3 +92,15 @@ test('it should return a map instance by its id', () => { renderHookResult = renderHook(() => useMap('unknown'), {wrapper}); expect(renderHookResult.result.current).toBe(null); }); + +test('it should log an error when used outside the APIProvider', () => { + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => {}); + + const res = renderHook(() => useMap()); + expect(res.result.current).toBe(null); + + expect(consoleErrorSpy).toHaveBeenCalled(); + expect(consoleErrorSpy.mock.calls).toMatchSnapshot(); +}); diff --git a/src/hooks/use-map.ts b/src/hooks/use-map.ts index f1356b4d..d05fbc1b 100644 --- a/src/hooks/use-map.ts +++ b/src/hooks/use-map.ts @@ -2,6 +2,7 @@ import {useContext} from 'react'; import {APIProviderContext} from '../components/api-provider'; import {GoogleMapsContext} from '../components/map'; +import {logErrorOnce} from '../libraries/errors'; /** * Retrieves a map-instance from the context. This is either an instance @@ -9,9 +10,22 @@ import {GoogleMapsContext} from '../components/map'; * Returns null if neither can be found. */ export const useMap = (id: string | null = null): google.maps.Map | null => { - const {mapInstances = {}} = useContext(APIProviderContext) || {}; + const ctx = useContext(APIProviderContext); const {map} = useContext(GoogleMapsContext) || {}; + if (ctx === null) { + logErrorOnce( + 'useMap(): failed to retrieve APIProviderContext. ' + + 'Make sure that the component exists and that the ' + + 'component you are calling `useMap()` from is a sibling of the ' + + '.' + ); + + return null; + } + + const {mapInstances} = ctx; + // if an id is specified, the corresponding map or null is returned if (id !== null) return mapInstances[id] || null;