Skip to content

Commit fefcd86

Browse files
committed
feat: Add useItem hook
1 parent 9684545 commit fefcd86

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

src/context/index.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import React, { useMemo, useContext, useState } from 'react';
1+
import React, { useMemo, useContext, useState, useCallback } from 'react';
22
import { createContext } from 'react';
33

44
import StacApi from '../stac-api';
55
import useStacApi from '../hooks/useStacApi';
6-
import type { CollectionsResponse } from '../types/stac';
6+
import type { CollectionsResponse, Item } from '../types/stac';
77
import { GenericObject } from '../types';
88

99
type StacApiContextType = {
1010
stacApi?: StacApi;
1111
collections?: CollectionsResponse;
1212
setCollections: (collections?: CollectionsResponse) => void;
13+
getItem: (id: string) => Item | undefined;
14+
addItem: (id: string, item: Item) => void;
1315
}
1416

1517
type StacApiProviderType = {
@@ -23,12 +25,21 @@ export const StacApiContext = createContext<StacApiContextType>({} as StacApiCon
2325
export function StacApiProvider({ children, apiUrl, options }: StacApiProviderType) {
2426
const { stacApi } = useStacApi(apiUrl, options);
2527
const [ collections, setCollections ] = useState<CollectionsResponse>();
28+
const [ items, setItems ] = useState(new Map<string, Item>());
29+
30+
const getItem = useCallback((id: string) => items.get(id), [items]);
31+
32+
const addItem = useCallback((itemPath: string, item: Item) => {
33+
setItems(new Map(items.set(itemPath, item)));
34+
}, [items]);
2635

2736
const contextValue = useMemo(() => ({
2837
stacApi,
2938
collections,
30-
setCollections
31-
}), [collections, stacApi]);
39+
setCollections,
40+
getItem,
41+
addItem
42+
}), [addItem, collections, getItem, stacApi]);
3243

3344
return (
3445
<StacApiContext.Provider value={contextValue}>
@@ -38,11 +49,19 @@ export function StacApiProvider({ children, apiUrl, options }: StacApiProviderTy
3849
}
3950

4051
export function useStacApiContext() {
41-
const { stacApi, collections, setCollections } = useContext(StacApiContext);
52+
const {
53+
stacApi,
54+
collections,
55+
setCollections,
56+
getItem,
57+
addItem
58+
} = useContext(StacApiContext);
4259

4360
return {
4461
stacApi,
4562
collections,
46-
setCollections
63+
setCollections,
64+
getItem,
65+
addItem
4766
};
4867
}

src/hooks/useItem.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import fetch from 'jest-fetch-mock';
2+
import { renderHook } from '@testing-library/react-hooks';
3+
import useItem from './useItem';
4+
import wrapper from './wrapper';
5+
6+
describe('useItem', () => {
7+
beforeEach(() => {
8+
fetch.resetMocks();
9+
});
10+
11+
it('queries item', async () => {
12+
fetch
13+
.mockResponseOnce(JSON.stringify({ id: 'abc' }))
14+
.mockResponseOnce(JSON.stringify({ id: 'abc', links: [] }))
15+
.mockResponseOnce(JSON.stringify({ id: 'abc' }));
16+
17+
const { result, waitForNextUpdate } = renderHook(
18+
() => useItem('https://fake-stac-api.net/items/abc'),
19+
{ wrapper }
20+
);
21+
await waitForNextUpdate();
22+
expect(result.current.item).toEqual({ id: 'abc' });
23+
expect(result.current.state).toEqual('IDLE');
24+
});
25+
});

src/hooks/useItem.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useState, useEffect} from 'react';
2+
import { Item } from '../types/stac';
3+
import { LoadingState } from '../types';
4+
import { useStacApiContext } from '../context';
5+
6+
type ItemHook = {
7+
item?: Item
8+
state: LoadingState
9+
}
10+
11+
function useItem(url: string): ItemHook {
12+
const { getItem, addItem } = useStacApiContext();
13+
const [ state, setState ] = useState<LoadingState>('IDLE');
14+
const [ item, setItem ] = useState<Item>();
15+
16+
useEffect(() => {
17+
setState('LOADING');
18+
new Promise((resolve) => {
19+
const i = getItem(url);
20+
if (i) {
21+
resolve(i);
22+
} else {
23+
fetch(url)
24+
.then(r => r.json())
25+
.then(r => {
26+
addItem(url, r);
27+
resolve(r);
28+
});
29+
}
30+
})
31+
.then(setItem)
32+
.finally(() => setState('IDLE'));
33+
}, [addItem, getItem, url]);
34+
35+
return {
36+
item,
37+
state
38+
};
39+
}
40+
41+
export default useItem;

0 commit comments

Comments
 (0)