Skip to content

Commit

Permalink
feat: resetQueries refetches active queries (#1397)
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronjensen authored Dec 13, 2020
1 parent 12d6714 commit df530d6
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 7 deletions.
7 changes: 5 additions & 2 deletions docs/src/pages/reference/QueryClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ property/state of the query.
This will notify subscribers — unlike `clear`, which removes all
subscribers — and reset the query to its pre-loaded state — unlike
`invalidateQueries`. If a query has `initialData`, the query's data will be
reset to that.
reset to that. If a query is active, it will be refetched.

```js
queryClient.resetQueries(queryKey, { exact: true })
Expand All @@ -294,10 +294,13 @@ queryClient.resetQueries(queryKey, { exact: true })

- `queryKey?: QueryKey`: [Query Keys](../guides/query-keys)
- `filters?: QueryFilters`: [Query Filters](../guides/query-filters)
- `resetOptions?: ResetOptions`:
- `throwOnError?: boolean`
- When set to `true`, this method will throw if any of the query refetch tasks fail.

**Returns**

This method does not return anything
This method returns a promise that resolves when all active queries have been refetched.

## `queryClient.isFetching`

Expand Down
18 changes: 13 additions & 5 deletions src/core/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
QueryObserverOptions,
QueryOptions,
RefetchOptions,
ResetOptions,
} from './types'
import type { QueryState, SetDataOptions } from './query'
import { QueryCache } from './queryCache'
Expand Down Expand Up @@ -132,15 +133,22 @@ export class QueryClient {
})
}

resetQueries(filters?: QueryFilters): void
resetQueries(queryKey?: QueryKey, filters?: QueryFilters): void
resetQueries(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): void {
const [filters] = parseFilterArgs(arg1, arg2)
resetQueries(filters?: QueryFilters, options?: ResetOptions): Promise<void>
resetQueries(queryKey?: QueryKey, filters?: QueryFilters, options?: ResetOptions): Promise<void>
resetQueries(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters | ResetOptions, arg3?: ResetOptions): Promise<void> {
const [filters, options] = parseFilterArgs(arg1, arg2, arg3)
const queryCache = this.queryCache
notifyManager.batch(() => {

const refetchFilters: QueryFilters = {
...filters,
active: true,
}

return notifyManager.batch(() => {
queryCache.findAll(filters).forEach(query => {
query.reset()
})
return this.refetchQueries(refetchFilters, options)
})
}

Expand Down
27 changes: 27 additions & 0 deletions src/core/tests/queryCache.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,33 @@ describe('queryCache', () => {
expect(state?.data).toEqual('initial')
})

test('resetQueries should refetch all active queries', async () => {
const key1 = queryKey()
const key2 = queryKey()
const queryFn1 = jest.fn()
const queryFn2 = jest.fn()
const testCache = new QueryCache()
const testClient = new QueryClient({ queryCache: testCache })
const observer1 = new QueryObserver(testClient, {
queryKey: key1,
queryFn: queryFn1,
enabled: true,
})
const observer2 = new QueryObserver(testClient, {
queryKey: key2,
queryFn: queryFn2,
enabled: false,
})
observer1.subscribe()
observer2.subscribe()
await testClient.resetQueries()
observer2.destroy()
observer1.destroy()
testCache.clear()
expect(queryFn1).toHaveBeenCalledTimes(2)
expect(queryFn2).toHaveBeenCalledTimes(0)
})

test('find should filter correctly', async () => {
const key = queryKey()
const testCache = new QueryCache()
Expand Down
4 changes: 4 additions & 0 deletions src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ export interface InvalidateOptions {
throwOnError?: boolean
}

export interface ResetOptions {
throwOnError?: boolean
}

export interface FetchNextPageOptions extends ResultOptions {
pageParam?: unknown
}
Expand Down
125 changes: 125 additions & 0 deletions src/react/tests/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2852,4 +2852,129 @@ describe('useQuery', () => {

expect(cancelFn).toHaveBeenCalled()
})

it('should update query state and refetch when reset with resetQueries', async () => {
const key = queryKey()
const states: UseQueryResult<number>[] = []
let count = 0

function Page() {
const state = useQuery(
key,
() => {
count++
return count
},
{ staleTime: Infinity }
)

states.push(state)

React.useEffect(() => {
setActTimeout(() => {
queryClient.resetQueries(key)
}, 10)
}, [])

return null
}

renderWithClient(queryClient, <Page />)

await sleep(100)

expect(states.length).toBe(4)
expect(states[0]).toMatchObject({
data: undefined,
isLoading: true,
isFetching: true,
isSuccess: false,
isStale: true,
})
expect(states[1]).toMatchObject({
data: 1,
isLoading: false,
isFetching: false,
isSuccess: true,
isStale: false,
})
expect(states[2]).toMatchObject({
data: undefined,
isLoading: true,
isFetching: true,
isSuccess: false,
isStale: true,
})
expect(states[3]).toMatchObject({
data: 2,
isLoading: false,
isFetching: false,
isSuccess: true,
isStale: false,
})
})

it('should update query state and not refetch when resetting a disabled query with resetQueries', async () => {
const key = queryKey()
const states: UseQueryResult<number>[] = []
let count = 0

function Page() {
const state = useQuery(
key,
() => {
count++
return count
},
{ staleTime: Infinity, enabled: false }
)

states.push(state)

React.useEffect(() => {
setActTimeout(() => {
state.refetch()
}, 0)
setActTimeout(() => {
queryClient.resetQueries(key)
}, 50)
}, [])

return null
}

renderWithClient(queryClient, <Page />)

await sleep(100)

expect(states.length).toBe(4)
expect(states[0]).toMatchObject({
data: undefined,
isLoading: false,
isFetching: false,
isSuccess: false,
isStale: true,
})
expect(states[1]).toMatchObject({
data: undefined,
isLoading: true,
isFetching: true,
isSuccess: false,
isStale: true,
})
expect(states[2]).toMatchObject({
data: 1,
isLoading: false,
isFetching: false,
isSuccess: true,
isStale: false,
})
expect(states[3]).toMatchObject({
data: undefined,
isLoading: false,
isFetching: false,
isSuccess: false,
isStale: true,
})
})
})

0 comments on commit df530d6

Please sign in to comment.