-
Notifications
You must be signed in to change notification settings - Fork 0
Clone use query state #12
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
base: main
Are you sure you want to change the base?
Changes from all commits
c4a8a12
b8d6851
4f00031
ff658ee
bea1eb5
a164794
f9017e6
7bf9b4e
05e18d3
efa8623
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,13 @@ | ||
| 'use client' | ||
| import * as React from 'react' | ||
| import { notifyManager } from '@tanstack/query-core' | ||
|
|
||
| import { useQueryClient } from './QueryClientProvider' | ||
| import { useQueryState } from './useQueryState' | ||
| import type { QueryClient, QueryFilters } from '@tanstack/query-core' | ||
|
|
||
| export function useIsFetching( | ||
| filters?: QueryFilters, | ||
| queryClient?: QueryClient, | ||
| ): number { | ||
| const client = useQueryClient(queryClient) | ||
| const queryCache = client.getQueryCache() | ||
|
|
||
| return React.useSyncExternalStore( | ||
| React.useCallback( | ||
| (onStoreChange) => | ||
| queryCache.subscribe(notifyManager.batchCalls(onStoreChange)), | ||
| [queryCache], | ||
| ), | ||
| () => client.isFetching(filters), | ||
| () => client.isFetching(filters), | ||
| ) | ||
| return useQueryState( | ||
| { filters: { ...filters, fetchStatus: 'fetching' } }, | ||
| queryClient, | ||
| ).length | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,67 @@ | ||||||||||||||||||
| 'use client' | ||||||||||||||||||
| import * as React from 'react' | ||||||||||||||||||
|
|
||||||||||||||||||
| import { notifyManager, replaceEqualDeep } from '@tanstack/query-core' | ||||||||||||||||||
| import { useQueryClient } from './QueryClientProvider' | ||||||||||||||||||
| import type { | ||||||||||||||||||
| DefaultError, | ||||||||||||||||||
| Query, | ||||||||||||||||||
| QueryCache, | ||||||||||||||||||
| QueryClient, | ||||||||||||||||||
| QueryFilters, | ||||||||||||||||||
| QueryKey, | ||||||||||||||||||
| QueryState, | ||||||||||||||||||
| } from '@tanstack/query-core' | ||||||||||||||||||
|
|
||||||||||||||||||
| type QueryStateOptions<TResult = QueryState> = { | ||||||||||||||||||
| filters?: QueryFilters | ||||||||||||||||||
| select?: (query: Query<unknown, DefaultError, unknown, QueryKey>) => TResult | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| function getResult<TResult = QueryState>( | ||||||||||||||||||
| queryCache: QueryCache, | ||||||||||||||||||
| options: QueryStateOptions<TResult>, | ||||||||||||||||||
| ): Array<TResult> { | ||||||||||||||||||
| return queryCache | ||||||||||||||||||
| .findAll(options.filters) | ||||||||||||||||||
| .map( | ||||||||||||||||||
| (query): TResult => | ||||||||||||||||||
| (options.select ? options.select(query) : query.state) as TResult, | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| export function useQueryState<TResult = QueryState>( | ||||||||||||||||||
| options: QueryStateOptions<TResult> = {}, | ||||||||||||||||||
| queryClient?: QueryClient, | ||||||||||||||||||
| ): Array<TResult> { | ||||||||||||||||||
| const queryCache = useQueryClient(queryClient).getQueryCache() | ||||||||||||||||||
| const optionsRef = React.useRef(options) | ||||||||||||||||||
| const result = React.useRef<Array<TResult>>() | ||||||||||||||||||
| if (!result.current) { | ||||||||||||||||||
| result.current = getResult(queryCache, options) | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+39
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Build break: useRef called without initial value (TS2554) TS error matches pipeline: Expected 1 arguments, but got 0. Initialize the ref to null (or undefined) and keep the first‑render assignment. Apply this diff: - const result = React.useRef<Array<TResult>>()
+ const result = React.useRef<Array<TResult> | null>(null)
if (!result.current) {
result.current = getResult(queryCache, options)
}This preserves runtime behavior and fixes typing by using non-null assertion on the hook return. 📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Actions: pr[error] 39-39: src/useQueryState.ts(39,24): TS2554: Expected 1 arguments, but got 0. 🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| React.useEffect(() => { | ||||||||||||||||||
| optionsRef.current = options | ||||||||||||||||||
| }, [options]) | ||||||||||||||||||
|
|
||||||||||||||||||
| return React.useSyncExternalStore( | ||||||||||||||||||
| React.useCallback( | ||||||||||||||||||
| (onStoreChange) => | ||||||||||||||||||
| queryCache.subscribe(notifyManager.batchCalls(onStoreChange)), | ||||||||||||||||||
| [queryCache], | ||||||||||||||||||
| ), | ||||||||||||||||||
| () => { | ||||||||||||||||||
| const nextResult = replaceEqualDeep( | ||||||||||||||||||
| result.current, | ||||||||||||||||||
| getResult(queryCache, optionsRef.current), | ||||||||||||||||||
| ) | ||||||||||||||||||
| if (result.current !== nextResult) { | ||||||||||||||||||
| result.current = nextResult | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return result.current | ||||||||||||||||||
| }, | ||||||||||||||||||
| () => result.current, | ||||||||||||||||||
| )! | ||||||||||||||||||
| } | ||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current typings for
useQueryStatecan lead to type-safety issues. If a consumer provides a generic type forTResultbut omits theselectfunction, the hook will return an array ofQueryStateobjects that are incorrectly typed asTResult[]. This can lead to runtime errors that are not caught by the TypeScript compiler.To improve type safety, I recommend using function overloads to ensure that if a custom
TResultis specified, aselectfunction returning that type is also required. This pattern is used elsewhere in React Query and would make this new API more robust.