@tanstack/query-core 5.47.0 → 5.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/legacy/focusManager.cjs.map +1 -1
- package/build/legacy/focusManager.js.map +1 -1
- package/build/legacy/{hydration-BZ2M_xzi.d.ts → hydration-ByKLEQMr.d.ts} +9 -4
- package/build/legacy/{hydration-CwKUqoFl.d.cts → hydration-DfqGmvZi.d.cts} +9 -4
- package/build/legacy/hydration.cjs +32 -15
- package/build/legacy/hydration.cjs.map +1 -1
- package/build/legacy/hydration.d.cts +1 -1
- package/build/legacy/hydration.d.ts +1 -1
- package/build/legacy/hydration.js +32 -15
- package/build/legacy/hydration.js.map +1 -1
- package/build/legacy/index.d.cts +1 -1
- package/build/legacy/index.d.ts +1 -1
- package/build/legacy/infiniteQueryBehavior.d.cts +1 -1
- package/build/legacy/infiniteQueryBehavior.d.ts +1 -1
- package/build/legacy/infiniteQueryObserver.d.cts +1 -1
- package/build/legacy/infiniteQueryObserver.d.ts +1 -1
- package/build/legacy/mutation.d.cts +1 -1
- package/build/legacy/mutation.d.ts +1 -1
- package/build/legacy/mutationCache.d.cts +1 -1
- package/build/legacy/mutationCache.d.ts +1 -1
- package/build/legacy/mutationObserver.d.cts +1 -1
- package/build/legacy/mutationObserver.d.ts +1 -1
- package/build/legacy/onlineManager.cjs.map +1 -1
- package/build/legacy/onlineManager.js.map +1 -1
- package/build/legacy/queriesObserver.d.cts +1 -1
- package/build/legacy/queriesObserver.d.ts +1 -1
- package/build/legacy/query.cjs +3 -1
- package/build/legacy/query.cjs.map +1 -1
- package/build/legacy/query.d.cts +1 -1
- package/build/legacy/query.d.ts +1 -1
- package/build/legacy/query.js +10 -2
- package/build/legacy/query.js.map +1 -1
- package/build/legacy/queryCache.d.cts +1 -1
- package/build/legacy/queryCache.d.ts +1 -1
- package/build/legacy/queryClient.cjs.map +1 -1
- package/build/legacy/queryClient.d.cts +1 -1
- package/build/legacy/queryClient.d.ts +1 -1
- package/build/legacy/queryClient.js.map +1 -1
- package/build/legacy/queryObserver.cjs +11 -9
- package/build/legacy/queryObserver.cjs.map +1 -1
- package/build/legacy/queryObserver.d.cts +1 -1
- package/build/legacy/queryObserver.d.ts +1 -1
- package/build/legacy/queryObserver.js +12 -9
- package/build/legacy/queryObserver.js.map +1 -1
- package/build/legacy/retryer.d.cts +1 -1
- package/build/legacy/retryer.d.ts +1 -1
- package/build/legacy/types.cjs.map +1 -1
- package/build/legacy/types.d.cts +1 -1
- package/build/legacy/types.d.ts +1 -1
- package/build/legacy/utils.cjs +5 -0
- package/build/legacy/utils.cjs.map +1 -1
- package/build/legacy/utils.d.cts +1 -1
- package/build/legacy/utils.d.ts +1 -1
- package/build/legacy/utils.js +4 -0
- package/build/legacy/utils.js.map +1 -1
- package/build/modern/focusManager.cjs.map +1 -1
- package/build/modern/focusManager.js.map +1 -1
- package/build/modern/{hydration-BZ2M_xzi.d.ts → hydration-ByKLEQMr.d.ts} +9 -4
- package/build/modern/{hydration-CwKUqoFl.d.cts → hydration-DfqGmvZi.d.cts} +9 -4
- package/build/modern/hydration.cjs +24 -8
- package/build/modern/hydration.cjs.map +1 -1
- package/build/modern/hydration.d.cts +1 -1
- package/build/modern/hydration.d.ts +1 -1
- package/build/modern/hydration.js +24 -8
- package/build/modern/hydration.js.map +1 -1
- package/build/modern/index.d.cts +1 -1
- package/build/modern/index.d.ts +1 -1
- package/build/modern/infiniteQueryBehavior.d.cts +1 -1
- package/build/modern/infiniteQueryBehavior.d.ts +1 -1
- package/build/modern/infiniteQueryObserver.d.cts +1 -1
- package/build/modern/infiniteQueryObserver.d.ts +1 -1
- package/build/modern/mutation.d.cts +1 -1
- package/build/modern/mutation.d.ts +1 -1
- package/build/modern/mutationCache.d.cts +1 -1
- package/build/modern/mutationCache.d.ts +1 -1
- package/build/modern/mutationObserver.d.cts +1 -1
- package/build/modern/mutationObserver.d.ts +1 -1
- package/build/modern/onlineManager.cjs.map +1 -1
- package/build/modern/onlineManager.js.map +1 -1
- package/build/modern/queriesObserver.d.cts +1 -1
- package/build/modern/queriesObserver.d.ts +1 -1
- package/build/modern/query.cjs +3 -1
- package/build/modern/query.cjs.map +1 -1
- package/build/modern/query.d.cts +1 -1
- package/build/modern/query.d.ts +1 -1
- package/build/modern/query.js +10 -2
- package/build/modern/query.js.map +1 -1
- package/build/modern/queryCache.d.cts +1 -1
- package/build/modern/queryCache.d.ts +1 -1
- package/build/modern/queryClient.cjs.map +1 -1
- package/build/modern/queryClient.d.cts +1 -1
- package/build/modern/queryClient.d.ts +1 -1
- package/build/modern/queryClient.js.map +1 -1
- package/build/modern/queryObserver.cjs +11 -9
- package/build/modern/queryObserver.cjs.map +1 -1
- package/build/modern/queryObserver.d.cts +1 -1
- package/build/modern/queryObserver.d.ts +1 -1
- package/build/modern/queryObserver.js +12 -9
- package/build/modern/queryObserver.js.map +1 -1
- package/build/modern/retryer.d.cts +1 -1
- package/build/modern/retryer.d.ts +1 -1
- package/build/modern/types.cjs.map +1 -1
- package/build/modern/types.d.cts +1 -1
- package/build/modern/types.d.ts +1 -1
- package/build/modern/utils.cjs +5 -0
- package/build/modern/utils.cjs.map +1 -1
- package/build/modern/utils.d.cts +1 -1
- package/build/modern/utils.d.ts +1 -1
- package/build/modern/utils.js +4 -0
- package/build/modern/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/hydration.test.tsx +85 -2
- package/src/__tests__/queryObserver.test.tsx +181 -0
- package/src/focusManager.ts +2 -2
- package/src/hydration.ts +41 -14
- package/src/onlineManager.ts +1 -1
- package/src/query.ts +10 -2
- package/src/queryClient.ts +3 -1
- package/src/queryObserver.ts +18 -9
- package/src/types.ts +12 -2
- package/src/utils.ts +13 -0
|
@@ -52,6 +52,173 @@ describe('queryObserver', () => {
|
|
|
52
52
|
unsubscribe()
|
|
53
53
|
})
|
|
54
54
|
|
|
55
|
+
describe('enabled is a callback that initially returns false', () => {
|
|
56
|
+
let observer: QueryObserver<string, Error, string, string, Array<string>>
|
|
57
|
+
let enabled: boolean
|
|
58
|
+
let count: number
|
|
59
|
+
let key: Array<string>
|
|
60
|
+
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
key = queryKey()
|
|
63
|
+
count = 0
|
|
64
|
+
enabled = false
|
|
65
|
+
|
|
66
|
+
observer = new QueryObserver(queryClient, {
|
|
67
|
+
queryKey: key,
|
|
68
|
+
staleTime: Infinity,
|
|
69
|
+
enabled: () => enabled,
|
|
70
|
+
queryFn: async () => {
|
|
71
|
+
await sleep(10)
|
|
72
|
+
count++
|
|
73
|
+
return 'data'
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('should not fetch on mount', () => {
|
|
79
|
+
const unsubscribe = observer.subscribe(vi.fn())
|
|
80
|
+
|
|
81
|
+
// Has not fetched and is not fetching since its disabled
|
|
82
|
+
expect(count).toBe(0)
|
|
83
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
84
|
+
status: 'pending',
|
|
85
|
+
fetchStatus: 'idle',
|
|
86
|
+
data: undefined,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
unsubscribe()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('should not be re-fetched when invalidated with refetchType: all', async () => {
|
|
93
|
+
const unsubscribe = observer.subscribe(vi.fn())
|
|
94
|
+
|
|
95
|
+
queryClient.invalidateQueries({ queryKey: key, refetchType: 'all' })
|
|
96
|
+
|
|
97
|
+
//So we still expect it to not have fetched and not be fetching
|
|
98
|
+
expect(count).toBe(0)
|
|
99
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
100
|
+
status: 'pending',
|
|
101
|
+
fetchStatus: 'idle',
|
|
102
|
+
data: undefined,
|
|
103
|
+
})
|
|
104
|
+
await waitFor(() => expect(count).toBe(0))
|
|
105
|
+
|
|
106
|
+
unsubscribe()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('should still trigger a fetch when refetch is called', async () => {
|
|
110
|
+
const unsubscribe = observer.subscribe(vi.fn())
|
|
111
|
+
|
|
112
|
+
expect(enabled).toBe(false)
|
|
113
|
+
|
|
114
|
+
//Not the same with explicit refetch, this will override enabled and trigger a fetch anyway
|
|
115
|
+
observer.refetch()
|
|
116
|
+
|
|
117
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
118
|
+
status: 'pending',
|
|
119
|
+
fetchStatus: 'fetching',
|
|
120
|
+
data: undefined,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
await waitFor(() => expect(count).toBe(1))
|
|
124
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
125
|
+
status: 'success',
|
|
126
|
+
fetchStatus: 'idle',
|
|
127
|
+
data: 'data',
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
unsubscribe()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test('should fetch if unsubscribed, then enabled returns true, and then re-subscribed', async () => {
|
|
134
|
+
let unsubscribe = observer.subscribe(vi.fn())
|
|
135
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
136
|
+
status: 'pending',
|
|
137
|
+
fetchStatus: 'idle',
|
|
138
|
+
data: undefined,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
unsubscribe()
|
|
142
|
+
|
|
143
|
+
enabled = true
|
|
144
|
+
|
|
145
|
+
unsubscribe = observer.subscribe(vi.fn())
|
|
146
|
+
|
|
147
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
148
|
+
status: 'pending',
|
|
149
|
+
fetchStatus: 'fetching',
|
|
150
|
+
data: undefined,
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
await waitFor(() => expect(count).toBe(1))
|
|
154
|
+
|
|
155
|
+
unsubscribe()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('should not be re-fetched if not subscribed to after enabled was toggled to true', async () => {
|
|
159
|
+
const unsubscribe = observer.subscribe(vi.fn())
|
|
160
|
+
|
|
161
|
+
// Toggle enabled
|
|
162
|
+
enabled = true
|
|
163
|
+
|
|
164
|
+
unsubscribe()
|
|
165
|
+
|
|
166
|
+
queryClient.invalidateQueries({ queryKey: key, refetchType: 'active' })
|
|
167
|
+
|
|
168
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
169
|
+
status: 'pending',
|
|
170
|
+
fetchStatus: 'idle',
|
|
171
|
+
data: undefined,
|
|
172
|
+
})
|
|
173
|
+
expect(count).toBe(0)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('should not be re-fetched if not subscribed to after enabled was toggled to true', async () => {
|
|
177
|
+
const unsubscribe = observer.subscribe(vi.fn())
|
|
178
|
+
|
|
179
|
+
// Toggle enabled
|
|
180
|
+
enabled = true
|
|
181
|
+
|
|
182
|
+
queryClient.invalidateQueries({ queryKey: key, refetchType: 'active' })
|
|
183
|
+
|
|
184
|
+
expect(observer.getCurrentResult()).toMatchObject({
|
|
185
|
+
status: 'pending',
|
|
186
|
+
fetchStatus: 'fetching',
|
|
187
|
+
data: undefined,
|
|
188
|
+
})
|
|
189
|
+
await waitFor(() => expect(count).toBe(1))
|
|
190
|
+
|
|
191
|
+
unsubscribe()
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('should handle that the enabled callback updates the return value', async () => {
|
|
195
|
+
const unsubscribe = observer.subscribe(vi.fn())
|
|
196
|
+
|
|
197
|
+
// Toggle enabled
|
|
198
|
+
enabled = true
|
|
199
|
+
|
|
200
|
+
queryClient.invalidateQueries({ queryKey: key, refetchType: 'inactive' })
|
|
201
|
+
|
|
202
|
+
//should not refetch since it was active and we only refetch inactive
|
|
203
|
+
await waitFor(() => expect(count).toBe(0))
|
|
204
|
+
|
|
205
|
+
queryClient.invalidateQueries({ queryKey: key, refetchType: 'active' })
|
|
206
|
+
|
|
207
|
+
//should refetch since it was active and we refetch active
|
|
208
|
+
await waitFor(() => expect(count).toBe(1))
|
|
209
|
+
|
|
210
|
+
// Toggle enabled
|
|
211
|
+
enabled = false
|
|
212
|
+
|
|
213
|
+
//should not refetch since it is not active and we only refetch active
|
|
214
|
+
queryClient.invalidateQueries({ queryKey: key, refetchType: 'active' })
|
|
215
|
+
|
|
216
|
+
await waitFor(() => expect(count).toBe(1))
|
|
217
|
+
|
|
218
|
+
unsubscribe()
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
55
222
|
test('should be able to read latest data when re-subscribing (but not re-fetching)', async () => {
|
|
56
223
|
const key = queryKey()
|
|
57
224
|
let count = 0
|
|
@@ -429,6 +596,20 @@ describe('queryObserver', () => {
|
|
|
429
596
|
expect(queryFn).toHaveBeenCalledTimes(0)
|
|
430
597
|
})
|
|
431
598
|
|
|
599
|
+
test('should not trigger a fetch when subscribed and disabled by callback', async () => {
|
|
600
|
+
const key = queryKey()
|
|
601
|
+
const queryFn = vi.fn<Array<unknown>, string>().mockReturnValue('data')
|
|
602
|
+
const observer = new QueryObserver(queryClient, {
|
|
603
|
+
queryKey: key,
|
|
604
|
+
queryFn,
|
|
605
|
+
enabled: () => false,
|
|
606
|
+
})
|
|
607
|
+
const unsubscribe = observer.subscribe(() => undefined)
|
|
608
|
+
await sleep(1)
|
|
609
|
+
unsubscribe()
|
|
610
|
+
expect(queryFn).toHaveBeenCalledTimes(0)
|
|
611
|
+
})
|
|
612
|
+
|
|
432
613
|
test('should not trigger a fetch when not subscribed', async () => {
|
|
433
614
|
const key = queryKey()
|
|
434
615
|
const queryFn = vi.fn<Array<unknown>, string>().mockReturnValue('data')
|
package/src/focusManager.ts
CHANGED
|
@@ -17,7 +17,7 @@ export class FocusManager extends Subscribable<Listener> {
|
|
|
17
17
|
super()
|
|
18
18
|
this.#setup = (onFocus) => {
|
|
19
19
|
// addEventListener does not exist in React Native, but window does
|
|
20
|
-
// eslint-disable-next-line
|
|
20
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
21
21
|
if (!isServer && window.addEventListener) {
|
|
22
22
|
const listener = () => onFocus()
|
|
23
23
|
// Listen to visibilitychange
|
|
@@ -78,7 +78,7 @@ export class FocusManager extends Subscribable<Listener> {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// document global can be unavailable in react native
|
|
81
|
-
// eslint-disable-next-line
|
|
81
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
82
82
|
return globalThis.document?.visibilityState !== 'hidden'
|
|
83
83
|
}
|
|
84
84
|
}
|
package/src/hydration.ts
CHANGED
|
@@ -13,15 +13,20 @@ import type { Query, QueryState } from './query'
|
|
|
13
13
|
import type { Mutation, MutationState } from './mutation'
|
|
14
14
|
|
|
15
15
|
// TYPES
|
|
16
|
+
type TransformerFn = (data: any) => any
|
|
17
|
+
function defaultTransformerFn(data: any): any {
|
|
18
|
+
return data
|
|
19
|
+
}
|
|
16
20
|
|
|
17
21
|
export interface DehydrateOptions {
|
|
22
|
+
serializeData?: TransformerFn
|
|
18
23
|
shouldDehydrateMutation?: (mutation: Mutation) => boolean
|
|
19
24
|
shouldDehydrateQuery?: (query: Query) => boolean
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
export interface HydrateOptions {
|
|
23
28
|
defaultOptions?: {
|
|
24
|
-
|
|
29
|
+
deserializeData?: TransformerFn
|
|
25
30
|
queries?: QueryOptions
|
|
26
31
|
mutations?: MutationOptions<unknown, DefaultError, unknown, unknown>
|
|
27
32
|
}
|
|
@@ -62,13 +67,21 @@ function dehydrateMutation(mutation: Mutation): DehydratedMutation {
|
|
|
62
67
|
// consuming the de/rehydrated data, typically with useQuery on the client.
|
|
63
68
|
// Sometimes it might make sense to prefetch data on the server and include
|
|
64
69
|
// in the html-payload, but not consume it on the initial render.
|
|
65
|
-
function dehydrateQuery(
|
|
70
|
+
function dehydrateQuery(
|
|
71
|
+
query: Query,
|
|
72
|
+
serializeData: TransformerFn,
|
|
73
|
+
): DehydratedQuery {
|
|
66
74
|
return {
|
|
67
|
-
state:
|
|
75
|
+
state: {
|
|
76
|
+
...query.state,
|
|
77
|
+
...(query.state.data !== undefined && {
|
|
78
|
+
data: serializeData(query.state.data),
|
|
79
|
+
}),
|
|
80
|
+
},
|
|
68
81
|
queryKey: query.queryKey,
|
|
69
82
|
queryHash: query.queryHash,
|
|
70
83
|
...(query.state.status === 'pending' && {
|
|
71
|
-
promise: query.promise?.catch((error) => {
|
|
84
|
+
promise: query.promise?.then(serializeData).catch((error) => {
|
|
72
85
|
if (process.env.NODE_ENV !== 'production') {
|
|
73
86
|
console.error(
|
|
74
87
|
`A query that was dehydrated as pending ended up rejecting. [${query.queryHash}]: ${error}; The error will be redacted in production builds`,
|
|
@@ -110,10 +123,17 @@ export function dehydrate(
|
|
|
110
123
|
client.getDefaultOptions().dehydrate?.shouldDehydrateQuery ??
|
|
111
124
|
defaultShouldDehydrateQuery
|
|
112
125
|
|
|
126
|
+
const serializeData =
|
|
127
|
+
options.serializeData ??
|
|
128
|
+
client.getDefaultOptions().dehydrate?.serializeData ??
|
|
129
|
+
defaultTransformerFn
|
|
130
|
+
|
|
113
131
|
const queries = client
|
|
114
132
|
.getQueryCache()
|
|
115
133
|
.getAll()
|
|
116
|
-
.flatMap((query) =>
|
|
134
|
+
.flatMap((query) =>
|
|
135
|
+
filterQuery(query) ? [dehydrateQuery(query, serializeData)] : [],
|
|
136
|
+
)
|
|
117
137
|
|
|
118
138
|
return { mutations, queries }
|
|
119
139
|
}
|
|
@@ -129,10 +149,14 @@ export function hydrate(
|
|
|
129
149
|
|
|
130
150
|
const mutationCache = client.getMutationCache()
|
|
131
151
|
const queryCache = client.getQueryCache()
|
|
152
|
+
const deserializeData =
|
|
153
|
+
options?.defaultOptions?.deserializeData ??
|
|
154
|
+
client.getDefaultOptions().hydrate?.deserializeData ??
|
|
155
|
+
defaultTransformerFn
|
|
132
156
|
|
|
133
|
-
// eslint-disable-next-line
|
|
157
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
134
158
|
const mutations = (dehydratedState as DehydratedState).mutations || []
|
|
135
|
-
// eslint-disable-next-line
|
|
159
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
136
160
|
const queries = (dehydratedState as DehydratedState).queries || []
|
|
137
161
|
|
|
138
162
|
mutations.forEach(({ state, ...mutationOptions }) => {
|
|
@@ -150,13 +174,19 @@ export function hydrate(
|
|
|
150
174
|
queries.forEach(({ queryKey, state, queryHash, meta, promise }) => {
|
|
151
175
|
let query = queryCache.get(queryHash)
|
|
152
176
|
|
|
177
|
+
const data =
|
|
178
|
+
state.data === undefined ? state.data : deserializeData(state.data)
|
|
179
|
+
|
|
153
180
|
// Do not hydrate if an existing query exists with newer data
|
|
154
181
|
if (query) {
|
|
155
182
|
if (query.state.dataUpdatedAt < state.dataUpdatedAt) {
|
|
156
183
|
// omit fetchStatus from dehydrated state
|
|
157
184
|
// so that query stays in its current fetchStatus
|
|
158
|
-
const { fetchStatus: _ignored, ...
|
|
159
|
-
query.setState(
|
|
185
|
+
const { fetchStatus: _ignored, ...serializedState } = state
|
|
186
|
+
query.setState({
|
|
187
|
+
...serializedState,
|
|
188
|
+
data,
|
|
189
|
+
})
|
|
160
190
|
}
|
|
161
191
|
} else {
|
|
162
192
|
// Restore query
|
|
@@ -173,19 +203,16 @@ export function hydrate(
|
|
|
173
203
|
// query being stuck in fetching state upon hydration
|
|
174
204
|
{
|
|
175
205
|
...state,
|
|
206
|
+
data,
|
|
176
207
|
fetchStatus: 'idle',
|
|
177
208
|
},
|
|
178
209
|
)
|
|
179
210
|
}
|
|
180
211
|
|
|
181
212
|
if (promise) {
|
|
182
|
-
const transformPromise =
|
|
183
|
-
client.getDefaultOptions().hydrate?.transformPromise
|
|
184
|
-
|
|
185
213
|
// Note: `Promise.resolve` required cause
|
|
186
214
|
// RSC transformed promises are not thenable
|
|
187
|
-
const initialPromise =
|
|
188
|
-
transformPromise?.(Promise.resolve(promise)) ?? promise
|
|
215
|
+
const initialPromise = Promise.resolve(promise).then(deserializeData)
|
|
189
216
|
|
|
190
217
|
// this doesn't actually fetch - it just creates a retryer
|
|
191
218
|
// which will re-use the passed `initialPromise`
|
package/src/onlineManager.ts
CHANGED
|
@@ -14,7 +14,7 @@ export class OnlineManager extends Subscribable<Listener> {
|
|
|
14
14
|
super()
|
|
15
15
|
this.#setup = (onOnline) => {
|
|
16
16
|
// addEventListener does not exist in React Native, but window does
|
|
17
|
-
// eslint-disable-next-line
|
|
17
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
18
18
|
if (!isServer && window.addEventListener) {
|
|
19
19
|
const onlineListener = () => onOnline(true)
|
|
20
20
|
const offlineListener = () => onOnline(false)
|
package/src/query.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ensureQueryFn,
|
|
3
|
+
noop,
|
|
4
|
+
replaceData,
|
|
5
|
+
resolveEnabled,
|
|
6
|
+
timeUntilStale,
|
|
7
|
+
} from './utils'
|
|
2
8
|
import { notifyManager } from './notifyManager'
|
|
3
9
|
import { canFetch, createRetryer, isCancelledError } from './retryer'
|
|
4
10
|
import { Removable } from './removable'
|
|
@@ -244,7 +250,9 @@ export class Query<
|
|
|
244
250
|
}
|
|
245
251
|
|
|
246
252
|
isActive(): boolean {
|
|
247
|
-
return this.observers.some(
|
|
253
|
+
return this.observers.some(
|
|
254
|
+
(observer) => resolveEnabled(observer.options.enabled, this) !== false,
|
|
255
|
+
)
|
|
248
256
|
}
|
|
249
257
|
|
|
250
258
|
isDisabled(): boolean {
|
package/src/queryClient.ts
CHANGED
|
@@ -13,10 +13,10 @@ import { focusManager } from './focusManager'
|
|
|
13
13
|
import { onlineManager } from './onlineManager'
|
|
14
14
|
import { notifyManager } from './notifyManager'
|
|
15
15
|
import { infiniteQueryBehavior } from './infiniteQueryBehavior'
|
|
16
|
-
import type { DataTag, NoInfer, OmitKeyof } from './types'
|
|
17
16
|
import type { QueryState } from './query'
|
|
18
17
|
import type {
|
|
19
18
|
CancelOptions,
|
|
19
|
+
DataTag,
|
|
20
20
|
DefaultError,
|
|
21
21
|
DefaultOptions,
|
|
22
22
|
DefaultedQueryObserverOptions,
|
|
@@ -29,6 +29,8 @@ import type {
|
|
|
29
29
|
MutationKey,
|
|
30
30
|
MutationObserverOptions,
|
|
31
31
|
MutationOptions,
|
|
32
|
+
NoInfer,
|
|
33
|
+
OmitKeyof,
|
|
32
34
|
QueryClientConfig,
|
|
33
35
|
QueryKey,
|
|
34
36
|
QueryObserverOptions,
|
package/src/queryObserver.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
isValidTimeout,
|
|
4
4
|
noop,
|
|
5
5
|
replaceData,
|
|
6
|
+
resolveEnabled,
|
|
6
7
|
resolveStaleTime,
|
|
7
8
|
shallowEqualObjects,
|
|
8
9
|
timeUntilStale,
|
|
@@ -149,9 +150,14 @@ export class QueryObserver<
|
|
|
149
150
|
|
|
150
151
|
if (
|
|
151
152
|
this.options.enabled !== undefined &&
|
|
152
|
-
typeof this.options.enabled !== 'boolean'
|
|
153
|
+
typeof this.options.enabled !== 'boolean' &&
|
|
154
|
+
typeof this.options.enabled !== 'function' &&
|
|
155
|
+
typeof resolveEnabled(this.options.enabled, this.#currentQuery) !==
|
|
156
|
+
'boolean'
|
|
153
157
|
) {
|
|
154
|
-
throw new Error(
|
|
158
|
+
throw new Error(
|
|
159
|
+
'Expected enabled to be a boolean or a callback that returns a boolean',
|
|
160
|
+
)
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
this.#updateQuery()
|
|
@@ -190,7 +196,8 @@ export class QueryObserver<
|
|
|
190
196
|
if (
|
|
191
197
|
mounted &&
|
|
192
198
|
(this.#currentQuery !== prevQuery ||
|
|
193
|
-
this.options.enabled !==
|
|
199
|
+
resolveEnabled(this.options.enabled, this.#currentQuery) !==
|
|
200
|
+
resolveEnabled(prevOptions.enabled, this.#currentQuery) ||
|
|
194
201
|
resolveStaleTime(this.options.staleTime, this.#currentQuery) !==
|
|
195
202
|
resolveStaleTime(prevOptions.staleTime, this.#currentQuery))
|
|
196
203
|
) {
|
|
@@ -203,7 +210,8 @@ export class QueryObserver<
|
|
|
203
210
|
if (
|
|
204
211
|
mounted &&
|
|
205
212
|
(this.#currentQuery !== prevQuery ||
|
|
206
|
-
this.options.enabled !==
|
|
213
|
+
resolveEnabled(this.options.enabled, this.#currentQuery) !==
|
|
214
|
+
resolveEnabled(prevOptions.enabled, this.#currentQuery) ||
|
|
207
215
|
nextRefetchInterval !== this.#currentRefetchInterval)
|
|
208
216
|
) {
|
|
209
217
|
this.#updateRefetchInterval(nextRefetchInterval)
|
|
@@ -377,7 +385,7 @@ export class QueryObserver<
|
|
|
377
385
|
|
|
378
386
|
if (
|
|
379
387
|
isServer ||
|
|
380
|
-
this.options.enabled === false ||
|
|
388
|
+
resolveEnabled(this.options.enabled, this.#currentQuery) === false ||
|
|
381
389
|
!isValidTimeout(this.#currentRefetchInterval) ||
|
|
382
390
|
this.#currentRefetchInterval === 0
|
|
383
391
|
) {
|
|
@@ -692,7 +700,7 @@ function shouldLoadOnMount(
|
|
|
692
700
|
options: QueryObserverOptions<any, any, any, any>,
|
|
693
701
|
): boolean {
|
|
694
702
|
return (
|
|
695
|
-
options.enabled !== false &&
|
|
703
|
+
resolveEnabled(options.enabled, query) !== false &&
|
|
696
704
|
query.state.data === undefined &&
|
|
697
705
|
!(query.state.status === 'error' && options.retryOnMount === false)
|
|
698
706
|
)
|
|
@@ -716,7 +724,7 @@ function shouldFetchOn(
|
|
|
716
724
|
(typeof options)['refetchOnWindowFocus'] &
|
|
717
725
|
(typeof options)['refetchOnReconnect'],
|
|
718
726
|
) {
|
|
719
|
-
if (options.enabled !== false) {
|
|
727
|
+
if (resolveEnabled(options.enabled, query) !== false) {
|
|
720
728
|
const value = typeof field === 'function' ? field(query) : field
|
|
721
729
|
|
|
722
730
|
return value === 'always' || (value !== false && isStale(query, options))
|
|
@@ -731,7 +739,8 @@ function shouldFetchOptionally(
|
|
|
731
739
|
prevOptions: QueryObserverOptions<any, any, any, any, any>,
|
|
732
740
|
): boolean {
|
|
733
741
|
return (
|
|
734
|
-
(query !== prevQuery ||
|
|
742
|
+
(query !== prevQuery ||
|
|
743
|
+
resolveEnabled(prevOptions.enabled, query) === false) &&
|
|
735
744
|
(!options.suspense || query.state.status !== 'error') &&
|
|
736
745
|
isStale(query, options)
|
|
737
746
|
)
|
|
@@ -742,7 +751,7 @@ function isStale(
|
|
|
742
751
|
options: QueryObserverOptions<any, any, any, any, any>,
|
|
743
752
|
): boolean {
|
|
744
753
|
return (
|
|
745
|
-
options.enabled !== false &&
|
|
754
|
+
resolveEnabled(options.enabled, query) !== false &&
|
|
746
755
|
query.isStaleByTime(resolveStaleTime(options.staleTime, query))
|
|
747
756
|
)
|
|
748
757
|
}
|
package/src/types.ts
CHANGED
|
@@ -54,6 +54,15 @@ export type StaleTime<
|
|
|
54
54
|
TQueryKey extends QueryKey = QueryKey,
|
|
55
55
|
> = number | ((query: Query<TQueryFnData, TError, TData, TQueryKey>) => number)
|
|
56
56
|
|
|
57
|
+
export type Enabled<
|
|
58
|
+
TQueryFnData = unknown,
|
|
59
|
+
TError = DefaultError,
|
|
60
|
+
TData = TQueryFnData,
|
|
61
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
62
|
+
> =
|
|
63
|
+
| boolean
|
|
64
|
+
| ((query: Query<TQueryFnData, TError, TData, TQueryKey>) => boolean)
|
|
65
|
+
|
|
57
66
|
export type QueryPersister<
|
|
58
67
|
T = unknown,
|
|
59
68
|
TQueryKey extends QueryKey = QueryKey,
|
|
@@ -253,11 +262,12 @@ export interface QueryObserverOptions<
|
|
|
253
262
|
'queryKey'
|
|
254
263
|
> {
|
|
255
264
|
/**
|
|
256
|
-
* Set this to `false` to disable automatic refetching when the query mounts or changes query keys.
|
|
265
|
+
* Set this to `false` or a function that returns `false` to disable automatic refetching when the query mounts or changes query keys.
|
|
257
266
|
* To refetch the query, use the `refetch` method returned from the `useQuery` instance.
|
|
267
|
+
* Accepts a boolean or function that returns a boolean.
|
|
258
268
|
* Defaults to `true`.
|
|
259
269
|
*/
|
|
260
|
-
enabled?:
|
|
270
|
+
enabled?: Enabled<TQueryFnData, TError, TQueryData, TQueryKey>
|
|
261
271
|
/**
|
|
262
272
|
* The time in milliseconds after data is considered stale.
|
|
263
273
|
* If set to `Infinity`, the data will never be considered stale.
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DefaultError,
|
|
3
|
+
Enabled,
|
|
3
4
|
FetchStatus,
|
|
4
5
|
MutationKey,
|
|
5
6
|
MutationStatus,
|
|
@@ -100,6 +101,18 @@ export function resolveStaleTime<
|
|
|
100
101
|
return typeof staleTime === 'function' ? staleTime(query) : staleTime
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
export function resolveEnabled<
|
|
105
|
+
TQueryFnData = unknown,
|
|
106
|
+
TError = DefaultError,
|
|
107
|
+
TData = TQueryFnData,
|
|
108
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
109
|
+
>(
|
|
110
|
+
enabled: undefined | Enabled<TQueryFnData, TError, TData, TQueryKey>,
|
|
111
|
+
query: Query<TQueryFnData, TError, TData, TQueryKey>,
|
|
112
|
+
): boolean | undefined {
|
|
113
|
+
return typeof enabled === 'function' ? enabled(query) : enabled
|
|
114
|
+
}
|
|
115
|
+
|
|
103
116
|
export function matchQuery(
|
|
104
117
|
filters: QueryFilters,
|
|
105
118
|
query: Query<any, any, any, any>,
|