Skip to content

Commit

Permalink
fix: output an error when useMap is called outside APIProvider (#117)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
usefulthink committed Dec 1, 2023
1 parent 9b788e1 commit 5c30c3d
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/hooks/__tests__/__snapshots__/use-map.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -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 <APIProvider> component exists and that the component you are calling \`useMap()\` from is a sibling of the <APIProvider>.",
],
]
`;
12 changes: 12 additions & 0 deletions src/hooks/__tests__/use-map.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
16 changes: 15 additions & 1 deletion src/hooks/use-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,30 @@ 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
* identified by id or the parent map instance if no id is specified.
* 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 <APIProvider> component exists and that the ' +
'component you are calling `useMap()` from is a sibling of the ' +
'<APIProvider>.'
);

return null;
}

const {mapInstances} = ctx;

// if an id is specified, the corresponding map or null is returned
if (id !== null) return mapInstances[id] || null;

Expand Down

0 comments on commit 5c30c3d

Please sign in to comment.