-
Notifications
You must be signed in to change notification settings - Fork 4
Migrate to TanStack Query for Data Fetching and Caching #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
a93b27c
docs: start architecture decision log and add ADR for fetch library d…
AliceR 9bccaaa
feat: migrate useCollections hook to TanStack Query
AliceR e40fcce
feat: enable TanStack Query DevTools browser extension
AliceR a04443b
feat: migrate useItem hook to TanStack Query
AliceR 3013303
docs(example): add item detail panel demonstrating useItem hook
AliceR 9999717
feat: migrate useStacApi hook to TanStack Query
AliceR 4d0f701
feat: migrate useStacSearch hook to TanStack Query
AliceR e0819c4
fix: externalize @tanstack/react-query in build and fix StacApiProvid…
AliceR 1dfbf58
fix: generate index.d.ts at dist root for proper TypeScript imports
AliceR 9f30e60
feat: optimize React Query cache keys
AliceR 6f5713d
fix: make DevTools opt-in to avoid conflicts with consuming apps
AliceR bc3550d
docs: add comment explaining gcTime in test wrapper
AliceR 0b4bcdc
refactor: replace Object.assign error pattern with ApiError class
AliceR 3ddb6f2
refactor: replace queryClient prop with auto-detecting parent QueryCl…
AliceR 363d53e
test: add StacApiProvider test coverage
AliceR fd431a3
refactor!: remove collections/items state from context
AliceR 802d1a2
Revert "refactor: replace queryClient prop with auto-detecting parent…
AliceR dc9c226
test: update tests to use queryClient prop when providing custom client
AliceR 2752ceb
example: add custom QueryClient with optimized caching
AliceR f86970f
docs: update QueryClient example and update API reference
AliceR 3236166
refactor!: replace LoadingState with React Query's isLoading/isFetching
AliceR c785dea
refactor: fetch single collection directly from /collections/{id} end…
AliceR 0109e59
fix: properly type reload as async across all hooks
AliceR 44882cb
refactor!: rename reload to refetch in hooks API
AliceR 9ecb267
docs: add migration guide for v1.0.0 (TanStack Query integration)
AliceR cc60b1d
chore: bump version to 1.0.0
AliceR 6b75fcc
fix: make date range parameters optional in useStacSearch hook
AliceR ec62ab4
fix: add comprehensive error handling for JSON parsing failures
AliceR 9cb92fa
fix: prevent memory leak in debounced submit function
AliceR 32272eb
docs: Add comment to clarify inner component pattern
AliceR 815d89e
Consolidate response parsing logic into handleStacResponse utility
AliceR 6849141
refactor: add base StacHook interface for type consistency
AliceR File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
26 changes: 26 additions & 0 deletions
26
docs/adr/0000-use-markdown-architectural-decision-records.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Use Markdown Architectural Decision Records | ||
|
|
||
| ## Context and Problem Statement | ||
|
|
||
| We want to record architectural decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. | ||
| Which format and structure should these records follow? | ||
|
|
||
| ## Considered Options | ||
|
|
||
| - [MADR](https://adr.github.io/madr/) 4.0.0 – The Markdown Architectural Decision Records | ||
| - [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" | ||
| - [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements | ||
| - Other templates listed at <https://github.com/joelparkerhenderson/architecture_decision_record> | ||
| - Formless – No conventions for file format and structure | ||
|
|
||
| ## Decision Outcome | ||
|
|
||
| Chosen option: "MADR 4.0.0", because | ||
|
|
||
| - Implicit assumptions should be made explicit. | ||
| Design documentation is important to enable people understanding the decisions later on. | ||
| See also ["A rational design process: How and why to fake it"](https://doi.org/10.1109/TSE.1986.6312940). | ||
| - MADR allows for structured capturing of any decision. | ||
| - The MADR format is lean and fits our development style. | ||
| - The MADR structure is comprehensible and facilitates usage & maintenance. | ||
| - The MADR project is vivid. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| --- | ||
| # These are optional metadata elements. Feel free to remove any of them. | ||
| status: "accepted" | ||
| date: 2025-09-18 | ||
| decision-makers: @gadomski @AliceR | ||
| --- | ||
|
|
||
| # Use a fetch library for caching | ||
|
|
||
| ## Context and Problem Statement | ||
|
|
||
| Currently, `stac-react` uses the native `fetch` API for all STAC requests, with no built-in caching or request deduplication. As the library is intended for use in applications that may navigate between many STAC resources, efficient caching and request management are important for performance and developer experience. | ||
|
|
||
| ## Decision Drivers | ||
|
|
||
| - Improve performance by caching repeated requests. | ||
| - Reduce network usage and latency. | ||
| - Provide a more robust API for request state, error handling, and background updates. | ||
| - Align with common React ecosystem practices. | ||
|
|
||
| ## Considered Options | ||
|
|
||
| - Continue using native `fetch` with custom caching logic. | ||
| - Use TanStack Query (`@tanstack/react-query`) for fetching and caching. | ||
| - Use another fetch/caching library (e.g., SWR, Axios with custom cache). | ||
|
|
||
| ## Decision Outcome | ||
|
|
||
| **Chosen option:** Use TanStack Query (`@tanstack/react-query`). | ||
|
|
||
| **Justification:** | ||
| TanStack Query is widely adopted, well-documented, and provides robust caching, request deduplication, background refetching, and React integration. It will make `stac-react` more attractive to downstream applications and reduce the need for custom caching logic. | ||
|
|
||
| ### Consequences | ||
|
|
||
| - **Good:** Improved performance and developer experience; less custom code for caching and request state. | ||
| - **Bad:** Adds a new dependency and requires refactoring existing hooks to use TanStack Query. | ||
|
|
||
| ### Confirmation | ||
|
|
||
| - Implementation will be confirmed by refactoring hooks to use TanStack Query and verifying caching behavior in tests and example app. | ||
| - Code review will ensure correct usage and integration. | ||
|
|
||
| ## Pros and Cons of the Options | ||
|
|
||
| ### TanStack Query | ||
|
|
||
| - **Good:** Robust caching, request deduplication, background updates, React integration. | ||
| - **Good:** Well-supported and documented. | ||
| - **Neutral:** Adds a dependency, but it is widely used. | ||
| - **Bad:** Requires refactoring and learning curve for maintainers. | ||
|
|
||
| ### Native Fetch | ||
|
|
||
| - **Good:** No new dependencies. | ||
| - **Bad:** No built-in caching, more custom code required, less robust for complex scenarios. | ||
|
|
||
| ### Other Libraries (SWR, Axios) | ||
|
|
||
| - **Good:** Some provide caching, but less feature-rich or less adopted for React. | ||
| - **Bad:** May require more custom integration. | ||
|
|
||
| ## More Information | ||
|
|
||
| - [TanStack Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview) | ||
| - This ADR will be revisited if TanStack Query no longer meets project needs or if a better alternative emerges. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # QueryClient Best Practice | ||
|
|
||
| stac-react relies on [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview) for data fetching and caching. To avoid duplicate React Query clients and potential version conflicts, stac-react lists `@tanstack/react-query` as a **peer dependency**. | ||
|
|
||
| ## Why peer dependency? | ||
|
|
||
| - Prevents multiple versions of React Query in your app. | ||
| - Ensures your app and stac-react share the same QueryClient instance. | ||
| - Follows best practices for React libraries that integrate with popular frameworks. | ||
|
|
||
| stac-react manages the QueryClient for you by default, but you can provide your own for advanced use cases. | ||
|
|
||
| **Important:** If your app uses multiple providers that require a TanStack QueryClient (such as `QueryClientProvider` and `StacApiProvider`), always use the same single QueryClient instance for all providers. This ensures that queries, mutations, and cache are shared across your app and prevents cache fragmentation or duplicate network requests. | ||
|
|
||
| **Example:** | ||
|
|
||
| ```jsx | ||
| import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
| import { StacApiProvider } from 'stac-react'; | ||
|
|
||
| const queryClient = new QueryClient(); | ||
|
|
||
| function App() { | ||
| return ( | ||
| <QueryClientProvider client={queryClient}> | ||
| <StacApiProvider apiUrl="https://my-stac-api.com" queryClient={queryClient}> | ||
| {/* ...your app... */} | ||
| </StacApiProvider> | ||
| </QueryClientProvider> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| If you do not pass the same QueryClient instance, each provider will maintain its own cache, which can lead to unexpected behavior. | ||
|
|
||
| ## TanStack Query DevTools Integration | ||
|
|
||
| stac-react automatically connects your QueryClient to the [TanStack Query DevTools browser extension](https://tanstack.com/query/latest/docs/framework/react/devtools) when running in development mode. This allows you to inspect queries, mutations, and cache directly in your browser without adding extra dependencies to your project. | ||
|
|
||
| **How it works:** | ||
|
|
||
| - In development (`process.env.NODE_ENV === 'development'`), stac-react exposes the QueryClient on `window.__TANSTACK_QUERY_CLIENT__`. | ||
| - The browser extension detects this and connects automatically. | ||
| - No code changes or additional dependencies are required. | ||
|
|
||
| > By default, React Query Devtools are only included in bundles when process.env.NODE_ENV === 'development', so you don't need to worry about excluding them during a production build. | ||
|
|
||
| **Alternative:** | ||
|
|
||
| - If you prefer an embedded/floating devtools panel, you can install and use the [TanStack Query Devtools React component](https://tanstack.com/query/latest/docs/framework/react/devtools#floating-devtools) in your app. This adds a UI panel directly to your app, but increases bundle size and dependencies. | ||
|
|
||
| For more details, see the [TanStack Query DevTools documentation](https://tanstack.com/query/latest/docs/framework/react/devtools). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import { useItem } from 'stac-react'; | ||
|
|
||
| import { H2 } from '../../components/headers'; | ||
| import Panel from '../../layout/Panel'; | ||
| import { Button } from '../../components/buttons'; | ||
|
|
||
| function ItemDetails({ item, onClose }) { | ||
| const itemUrl = item.links.find((r) => r.rel === 'self')?.href; | ||
| const { item: newItem, isLoading, error, reload } = useItem(itemUrl); | ||
|
|
||
| return ( | ||
| <Panel className="grid grid-rows-[1fr_min-content] p-4 h-[calc(100vh_-_90px)] overflow-y-scroll w-full overflow-hidden"> | ||
| <div className="w-full overflow-hidden"> | ||
| <div className="flex flex-wrap items-start gap-2"> | ||
| <H2 className="whitespace-normal break-words flex-1">Selected Item</H2> | ||
| <Button | ||
| type="button" | ||
| onClick={onClose} | ||
| aria-label="Close selected item panel" | ||
| title="Close" | ||
| className="p-2 rounded hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-300" | ||
| > | ||
| <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| className="w-5 h-5" | ||
| viewBox="0 0 24 24" | ||
| fill="none" | ||
| stroke="currentColor" | ||
| > | ||
| <path | ||
| strokeWidth="2" | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| d="M6 18L18 6M6 6l12 12" | ||
| /> | ||
| </svg> | ||
| </Button> | ||
| </div> | ||
| {isLoading && <p className="whitespace-normal break-words">Loading...</p>} | ||
| {error && <p className="whitespace-normal break-words">{error}</p>} | ||
| {newItem && ( | ||
| <pre className="bg-gray-100 p-2 rounded w-full whitespace-pre-wrap break-words overflow-x-auto text-xs"> | ||
| {JSON.stringify(newItem, null, 2)} | ||
| </pre> | ||
| )} | ||
| </div> | ||
| <div className="grid grid-cols-2 gap-4"> | ||
| <Button type="button" onClick={reload}> | ||
| Reload | ||
| </Button> | ||
| </div> | ||
| </Panel> | ||
| ); | ||
| } | ||
| export default ItemDetails; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.