Skip to content

Commit 3236166

Browse files
committed
refactor!: replace LoadingState with React Query's isLoading/isFetching
BREAKING CHANGE: Hooks no longer return `state` property. Use `isLoading` and `isFetching` booleans instead. - Update hooks to return React Query's isLoading/isFetching directly - Update all tests to check isLoading instead of state - Update example app (Main/index.jsx, ItemDetails.jsx) to use isLoading - Update README with new API documentation Eliminates duplicate state management and provides more granular loading control.
1 parent f86970f commit 3236166

File tree

12 files changed

+88
-104
lines changed

12 files changed

+88
-104
lines changed

README.md

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,23 @@ const { collections } = useCollections();
143143

144144
#### Return values
145145

146-
| Option | Type | Description |
147-
| ------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
148-
| `collections` | `array` | A list of collections available from the STAC catalog. Is `null` if collections have not been retrieved. |
149-
| `state` | `str` | The status of the request. `"IDLE"` before and after the request is sent or received. `"LOADING"` when the request is in progress. |
150-
| `reload` | `function` | Callback function to trigger a reload of collections. |
151-
| `error` | [`Error`](#error) | Error information if the last request was unsuccessful. `undefined` if the last request was successful. |
146+
| Option | Type | Description |
147+
| ------------- | ----------------- | -------------------------------------------------------------------------------------------------------- |
148+
| `collections` | `array` | A list of collections available from the STAC catalog. Is `null` if collections have not been retrieved. |
149+
| `isLoading` | `boolean` | `true` when the initial request is in progress. `false` once data is loaded or an error occurred. |
150+
| `isFetching` | `boolean` | `true` when any request is in progress (including background refetches). `false` otherwise. |
151+
| `reload` | `function` | Callback function to trigger a reload of collections. |
152+
| `error` | [`Error`](#error) | Error information if the last request was unsuccessful. `undefined` if the last request was successful. |
152153

153154
#### Example
154155

155156
```js
156157
import { useCollections } from "stac-react";
157158

158159
function CollectionList() {
159-
const { collections, state } = useCollections();
160+
const { collections, isLoading } = useCollections();
160161

161-
if (state === "LOADING") {
162+
if (isLoading) {
162163
return <p>Loading collections...</p>
163164
}
164165

@@ -198,22 +199,23 @@ const { collection } = useCollection(id);
198199

199200
#### Return values
200201

201-
| Option | Type | Description |
202-
| ------------ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
203-
| `collection` | `object` | The collection matching the provided ID. Is `null` if collection has not been retrieved. |
204-
| `state` | `str` | The status of the request. `"IDLE"` before and after the request is sent or received. `"LOADING"` when the request is in progress. |
205-
| `reload` | `function` | Callback function to trigger a reload of the collection. |
206-
| `error` | [`Error`](#error) | Error information if the last request was unsuccessful. `undefined` if the last request was successful. |
202+
| Option | Type | Description |
203+
| ------------ | ----------------- | ------------------------------------------------------------------------------------------------------- |
204+
| `collection` | `object` | The collection matching the provided ID. Is `null` if collection has not been retrieved. |
205+
| `isLoading` | `boolean` | `true` when the initial request is in progress. `false` once data is loaded or an error occurred. |
206+
| `isFetching` | `boolean` | `true` when any request is in progress (including background refetches). `false` otherwise. |
207+
| `reload` | `function` | Callback function to trigger a reload of the collection. |
208+
| `error` | [`Error`](#error) | Error information if the last request was unsuccessful. `undefined` if the last request was successful. |
207209

208210
#### Example
209211

210212
```js
211213
import { useCollection } from 'stac-react';
212214

213215
function Collection() {
214-
const { collection, state } = useCollection('collection_id');
216+
const { collection, isLoading } = useCollection('collection_id');
215217

216-
if (state === 'LOADING') {
218+
if (isLoading) {
217219
return <p>Loading collection...</p>;
218220
}
219221

@@ -251,22 +253,23 @@ const { item } = useItem(url);
251253

252254
#### Return values
253255

254-
| Option | Type | Description |
255-
| -------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
256-
| `item` | `object` | The item matching the provided URL. |
257-
| `state` | `str` | The status of the request. `"IDLE"` before and after the request is sent or received. `"LOADING"` when the request is in progress. |
258-
| `reload` | `function` | Callback function to trigger a reload of the item. |
259-
| `error` | [`Error`](#error) | Error information if the last request was unsuccessful. `undefined` if the last request was successful. |
256+
| Option | Type | Description |
257+
| ------------ | ----------------- | ------------------------------------------------------------------------------------------------------- |
258+
| `item` | `object` | The item matching the provided URL. |
259+
| `isLoading` | `boolean` | `true` when the initial request is in progress. `false` once data is loaded or an error occurred. |
260+
| `isFetching` | `boolean` | `true` when any request is in progress (including background refetches). `false` otherwise. |
261+
| `reload` | `function` | Callback function to trigger a reload of the item. |
262+
| `error` | [`Error`](#error) | Error information if the last request was unsuccessful. `undefined` if the last request was successful. |
260263

261264
#### Examples
262265

263266
```js
264267
import { useItem } from 'stac-react';
265268

266269
function Item() {
267-
const { item, state } = useItem('https://stac-catalog.com/items/abc123');
270+
const { item, isLoading } = useItem('https://stac-catalog.com/items/abc123');
268271

269-
if (state === 'LOADING') {
272+
if (isLoading) {
270273
return <p>Loading item...</p>;
271274
}
272275

@@ -316,7 +319,8 @@ const { results } = useStacSearch();
316319
| `limit` | `number` | The number of results returned per result page. |
317320
| `setLimit(limit)` | `function` | Callback to set `limit`. `limit` must be a `number`, or `undefined` to reset. |
318321
| `results` | `object` | The result of the last search query; a [GeoJSON `FeatureCollection` with additional members](https://github.com/radiantearth/stac-api-spec/blob/v1.0.0-rc.2/fragments/itemcollection/README.md). `undefined` if the search request has not been submitted, or if there was an error. |
319-
| `state` | `string` | The status of the request. `"IDLE"` before and after the request is sent or received. `"LOADING"` when the request is in progress. |
322+
| `isLoading` | `boolean` | `true` when the initial request is in progress. `false` once data is loaded or an error occurred. |
323+
| `isFetching` | `boolean` | `true` when any request is in progress (including background refetches and pagination). `false` otherwise. |
320324
| `error` | [`Error`](#error) | Error information if the last request was unsuccessful. `undefined` if the last request was successful. |
321325
| `nextPage` | `function` | Callback function to load the next page of results. Is `undefined` if the last page is the currently loaded. |
322326
| `previousPage` | `function` | Callback function to load the previous page of results. Is `undefined` if the first page is the currently loaded. |

example/src/pages/Main/ItemDetails.jsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import { Button } from '../../components/buttons';
66

77
function ItemDetails({ item, onClose }) {
88
const itemUrl = item.links.find((r) => r.rel === 'self')?.href;
9-
const { item: newItem, state, error, reload } = useItem(itemUrl);
10-
11-
const isLoading = state === 'LOADING';
9+
const { item: newItem, isLoading, error, reload } = useItem(itemUrl);
1210

1311
return (
1412
<Panel className="grid grid-rows-[1fr_min-content] p-4 h-[calc(100vh_-_90px)] overflow-y-scroll w-full overflow-hidden">

example/src/pages/Main/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function Main() {
2727
setDateRangeTo,
2828
submit,
2929
results,
30-
state,
30+
isLoading,
3131
error,
3232
nextPage,
3333
previousPage,
@@ -74,7 +74,7 @@ function Main() {
7474
) : (
7575
<ItemList
7676
items={results}
77-
isLoading={state === 'LOADING'}
77+
isLoading={isLoading}
7878
error={error && 'Error loading results'}
7979
nextPage={nextPage}
8080
previousPage={previousPage}

src/hooks/useCollection.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('useCollection', () => {
2121
);
2222

2323
const { result } = renderHook(() => useCollection('abc'), { wrapper });
24-
await waitFor(() => expect(result.current.state).toBe('IDLE'));
24+
await waitFor(() => expect(result.current.isLoading).toEqual(false));
2525
await waitFor(() =>
2626
expect(result.current.collection).toEqual({ id: 'abc', title: 'Collection A' })
2727
);

src/hooks/useCollection.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { useMemo } from 'react';
22

3-
import type { ApiErrorType, LoadingState } from '../types';
3+
import type { ApiErrorType } from '../types';
44
import type { Collection } from '../types/stac';
55
import useCollections from './useCollections';
66

77
type StacCollectionHook = {
88
collection?: Collection;
9-
state: LoadingState;
9+
isLoading: boolean;
10+
isFetching: boolean;
1011
error?: ApiErrorType;
1112
reload: () => void;
1213
};
1314

1415
function useCollection(collectionId: string): StacCollectionHook {
15-
const { collections, state, error: requestError, reload } = useCollections();
16+
const { collections, isLoading, isFetching, error: requestError, reload } = useCollections();
1617

1718
const collection = useMemo(() => {
1819
return collections?.collections.find(({ id }) => id === collectionId);
@@ -31,7 +32,8 @@ function useCollection(collectionId: string): StacCollectionHook {
3132

3233
return {
3334
collection,
34-
state,
35+
isLoading,
36+
isFetching,
3537
error,
3638
reload,
3739
};

src/hooks/useCollections.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('useCollections', () => {
1919
expect(fetch.mock.calls[1][0]).toEqual('https://fake-stac-api.net/collections')
2020
);
2121
await waitFor(() => expect(result.current.collections).toEqual({ data: '12345' }));
22-
await waitFor(() => expect(result.current.state).toEqual('IDLE'));
22+
await waitFor(() => expect(result.current.isLoading).toEqual(false));
2323
});
2424

2525
it('reloads collections', async () => {
@@ -30,7 +30,7 @@ describe('useCollections', () => {
3030

3131
const { result } = renderHook(() => useCollections(), { wrapper });
3232
await waitFor(() => expect(result.current.collections).toEqual({ data: 'original' }));
33-
await waitFor(() => expect(result.current.state).toEqual('IDLE'));
33+
await waitFor(() => expect(result.current.isLoading).toEqual(false));
3434

3535
act(() => result.current.reload());
3636

src/hooks/useCollections.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useEffect, useState, useMemo } from 'react';
1+
import { useMemo } from 'react';
22
import { useQuery } from '@tanstack/react-query';
3-
import { type ApiErrorType, type LoadingState } from '../types';
3+
import { type ApiErrorType } from '../types';
44
import type { CollectionsResponse } from '../types/stac';
55
import debounce from '../utils/debounce';
66
import { ApiError } from '../utils/ApiError';
@@ -10,13 +10,13 @@ import { useStacApiContext } from '../context/useStacApiContext';
1010
type StacCollectionsHook = {
1111
collections?: CollectionsResponse;
1212
reload: () => void;
13-
state: LoadingState;
13+
isLoading: boolean;
14+
isFetching: boolean;
1415
error?: ApiErrorType;
1516
};
1617

1718
function useCollections(): StacCollectionsHook {
1819
const { stacApi } = useStacApiContext();
19-
const [state, setState] = useState<LoadingState>('IDLE');
2020

2121
const fetchCollections = async (): Promise<CollectionsResponse> => {
2222
if (!stacApi) throw new Error('No STAC API configured');
@@ -49,20 +49,11 @@ function useCollections(): StacCollectionsHook {
4949

5050
const reload = useMemo(() => debounce(refetch), [refetch]);
5151

52-
useEffect(() => {
53-
if (!stacApi) {
54-
setState('IDLE');
55-
} else if (isLoading || isFetching) {
56-
setState('LOADING');
57-
} else {
58-
setState('IDLE');
59-
}
60-
}, [stacApi, isLoading, isFetching]);
61-
6252
return {
6353
collections,
6454
reload,
65-
state,
55+
isLoading,
56+
isFetching,
6657
error: error as ApiErrorType,
6758
};
6859
}

src/hooks/useItem.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe('useItem', () => {
1717
wrapper,
1818
});
1919
await waitFor(() => expect(result.current.item).toEqual({ id: 'abc' }));
20-
await waitFor(() => expect(result.current.state).toEqual('IDLE'));
20+
await waitFor(() => expect(result.current.isLoading).toEqual(false));
2121
});
2222

2323
it('handles error with JSON response', async () => {

src/hooks/useItem.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1-
import { useEffect, useState } from 'react';
21
import { useQuery } from '@tanstack/react-query';
32
import { Item } from '../types/stac';
4-
import { type ApiErrorType, type LoadingState } from '../types';
3+
import { type ApiErrorType } from '../types';
54
import { useStacApiContext } from '../context/useStacApiContext';
65
import { ApiError } from '../utils/ApiError';
76
import { generateItemQueryKey } from '../utils/queryKeys';
87

98
type ItemHook = {
109
item?: Item;
11-
state: LoadingState;
10+
isLoading: boolean;
11+
isFetching: boolean;
1212
error?: ApiErrorType;
1313
reload: () => void;
1414
};
1515

1616
function useItem(url: string): ItemHook {
1717
const { stacApi } = useStacApiContext();
18-
const [state, setState] = useState<LoadingState>('IDLE');
1918

2019
const fetchItem = async (): Promise<Item> => {
2120
if (!stacApi) throw new Error('No STAC API configured');
@@ -46,17 +45,10 @@ function useItem(url: string): ItemHook {
4645
retry: false,
4746
});
4847

49-
useEffect(() => {
50-
if (isLoading || isFetching) {
51-
setState('LOADING');
52-
} else {
53-
setState('IDLE');
54-
}
55-
}, [isLoading, isFetching]);
56-
5748
return {
5849
item,
59-
state,
50+
isLoading,
51+
isFetching,
6052
error: error as ApiErrorType,
6153
reload: refetch as () => void,
6254
};

0 commit comments

Comments
 (0)