Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/src/pages/reference/QueryClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Its available methods are:
- [`prefetchInfiniteQuery`](#queryclientprefetchinfinitequery)
- [`getQueryData`](#queryclientgetquerydata)
- [`setQueryData`](#queryclientsetquerydata)
- [`setQueriesData`](#queryclientsetqueriesdata)
- [`getQueryState`](#queryclientgetquerystate)
- [`invalidateQueries`](#queryclientinvalidatequeries)
- [`refetchQueries`](#queryclientrefetchqueries)
Expand Down Expand Up @@ -187,7 +188,7 @@ queryClient.setQueryData(queryKey, updater)
**Options**

- `queryKey: QueryKey`: [Query Keys](../guides/query-keys)
- `updater: unknown | (oldData: TData | undefined) => TData`
- `updater: TData | (oldData: TData | undefined) => TData`
- If non-function is passed, the data will be updated to this value
- If a function is passed, it will receive the old data value and be expected to return a new one.

Expand Down Expand Up @@ -219,6 +220,22 @@ console.log(state.dataUpdatedAt)
- `queryKey?: QueryKey`: [Query Keys](../guides/query-keys)
- `filters?: QueryFilters`: [Query Filters](../guides/filters#query-filters)

## `queryClient.setQueriesData`

`setQueriesData` is a synchronous function that can be used to immediately update cached data of multiple queries. Only queries that match the passed queryKey or queryFilter will be updated - no new cache entries will be created. Under the hood, [`setQueryData`](#queryclientsetquerydata) is called for each query.

```js
queryClient.setQueriesData(queryKey | filters, updater)
```

**Options**

- `queryKey: QueryKey`: [Query Keys](../guides/query-keys) | `filters: QueryFilters`: [Query Filters](../guides/filters#query-filters)
- if a queryKey is passed as first argument, queryKeys fuzzily matching this param will be updated
- if a filter is passed, queryKeys matching the filter will be updated
- `updater: TData | (oldData: TData | undefined) => TData`
- the [setQueryData](#queryclientsetquerydata) updater function or new data, will be called for each matching queryKey

## `queryClient.invalidateQueries`

The `invalidateQueries` method can be used to invalidate and refetch single or multiple queries in the cache based on their query keys or any other functionally accessible property/state of the query. By default, all matching queries are immediately marked as invalid and active queries are refetched in the background.
Expand Down
27 changes: 27 additions & 0 deletions src/core/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,33 @@ export class QueryClient {
.setData(updater, options)
}

setQueriesData<TData>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we are not creating queries like with setQueryData, because we only update matching keys, would updateQueries or updateQueriesData be a better name?

this was also mentioned by @tannerlinsley here: #135 (comment)

but I don't think it made it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the current name is fine as long as people realize that it will not create queries.

queryKey: QueryKey,
updater: Updater<TData | undefined, TData>,
options?: SetDataOptions
): [QueryKey, TData][]

setQueriesData<TData>(
filters: QueryFilters,
Comment on lines +132 to +133
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't create an overload that accepts queryKey and filters, even though some other functions have this, because the updater is required so the signature would be very weird. I also don't think it's important because the filters also have the option to pass a queryKey.

updater: Updater<TData | undefined, TData>,
options?: SetDataOptions
): [QueryKey, TData][]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this is the best return type - wanted to provide a way to show which key actually had data set, as calling setQueriesData on a filter that doesn't match anything will not set anything


setQueriesData<TData>(
queryKeyOrFilters: QueryKey | QueryFilters,
updater: Updater<TData | undefined, TData>,
options?: SetDataOptions
): [QueryKey, TData][] {
return notifyManager.batch(() =>
this.getQueryCache()
.findAll(queryKeyOrFilters)
.map(({ queryKey }) => [
queryKey,
this.setQueryData<TData>(queryKey, updater, options),
])
)
}

getQueryState<TData = unknown, TError = undefined>(
queryKey: QueryKey,
filters?: QueryFilters
Expand Down
38 changes: 38 additions & 0 deletions src/core/tests/queryClient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,44 @@ describe('queryClient', () => {
})
})

describe('setQueriesData', () => {
test('should update all existing, matching queries', () => {
queryClient.setQueryData(['key', 1], 1)
queryClient.setQueryData(['key', 2], 2)

const result = queryClient.setQueriesData<number>('key', old => old! + 5)

expect(result).toEqual([
[['key', 1], 6],
[['key', 2], 7],
])
expect(queryClient.getQueryData(['key', 1])).toBe(6)
expect(queryClient.getQueryData(['key', 2])).toBe(7)
})

test('should accept queryFilters', () => {
queryClient.setQueryData(['key', 1], 1)
queryClient.setQueryData(['key', 2], 2)
const query1 = queryCache.find(['key', 1])!

const result = queryClient.setQueriesData<number>(
{ predicate: query => query === query1 },
old => old! + 5
)

expect(result).toEqual([[['key', 1], 6]])
expect(queryClient.getQueryData(['key', 1])).toBe(6)
expect(queryClient.getQueryData(['key', 2])).toBe(2)
})

test('should not update non existing queries', () => {
const result = queryClient.setQueriesData<string>('key', 'data')

expect(result).toEqual([])
expect(queryClient.getQueryData('key')).toBe(undefined)
})
})

describe('getQueryData', () => {
test('should return the query data if the query is found', () => {
const key = queryKey()
Expand Down