@tanstack/query-core 4.0.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.
Files changed (85) hide show
  1. package/build/cjs/focusManager.js +101 -0
  2. package/build/cjs/focusManager.js.map +1 -0
  3. package/build/cjs/hydration.js +112 -0
  4. package/build/cjs/hydration.js.map +1 -0
  5. package/build/cjs/index.js +51 -0
  6. package/build/cjs/index.js.map +1 -0
  7. package/build/cjs/infiniteQueryBehavior.js +160 -0
  8. package/build/cjs/infiniteQueryBehavior.js.map +1 -0
  9. package/build/cjs/infiniteQueryObserver.js +92 -0
  10. package/build/cjs/infiniteQueryObserver.js.map +1 -0
  11. package/build/cjs/logger.js +18 -0
  12. package/build/cjs/logger.js.map +1 -0
  13. package/build/cjs/mutation.js +258 -0
  14. package/build/cjs/mutation.js.map +1 -0
  15. package/build/cjs/mutationCache.js +99 -0
  16. package/build/cjs/mutationCache.js.map +1 -0
  17. package/build/cjs/mutationObserver.js +130 -0
  18. package/build/cjs/mutationObserver.js.map +1 -0
  19. package/build/cjs/notifyManager.js +114 -0
  20. package/build/cjs/notifyManager.js.map +1 -0
  21. package/build/cjs/onlineManager.js +100 -0
  22. package/build/cjs/onlineManager.js.map +1 -0
  23. package/build/cjs/queriesObserver.js +170 -0
  24. package/build/cjs/queriesObserver.js.map +1 -0
  25. package/build/cjs/query.js +474 -0
  26. package/build/cjs/query.js.map +1 -0
  27. package/build/cjs/queryCache.js +140 -0
  28. package/build/cjs/queryCache.js.map +1 -0
  29. package/build/cjs/queryClient.js +357 -0
  30. package/build/cjs/queryClient.js.map +1 -0
  31. package/build/cjs/queryObserver.js +521 -0
  32. package/build/cjs/queryObserver.js.map +1 -0
  33. package/build/cjs/removable.js +47 -0
  34. package/build/cjs/removable.js.map +1 -0
  35. package/build/cjs/retryer.js +177 -0
  36. package/build/cjs/retryer.js.map +1 -0
  37. package/build/cjs/subscribable.js +43 -0
  38. package/build/cjs/subscribable.js.map +1 -0
  39. package/build/cjs/utils.js +356 -0
  40. package/build/cjs/utils.js.map +1 -0
  41. package/build/esm/index.js +3077 -0
  42. package/build/esm/index.js.map +1 -0
  43. package/build/stats-html.html +2689 -0
  44. package/build/umd/index.development.js +3106 -0
  45. package/build/umd/index.development.js.map +1 -0
  46. package/build/umd/index.production.js +12 -0
  47. package/build/umd/index.production.js.map +1 -0
  48. package/package.json +25 -0
  49. package/src/focusManager.ts +89 -0
  50. package/src/hydration.ts +164 -0
  51. package/src/index.ts +35 -0
  52. package/src/infiniteQueryBehavior.ts +214 -0
  53. package/src/infiniteQueryObserver.ts +159 -0
  54. package/src/logger.native.ts +11 -0
  55. package/src/logger.ts +9 -0
  56. package/src/mutation.ts +349 -0
  57. package/src/mutationCache.ts +157 -0
  58. package/src/mutationObserver.ts +195 -0
  59. package/src/notifyManager.ts +96 -0
  60. package/src/onlineManager.ts +89 -0
  61. package/src/queriesObserver.ts +211 -0
  62. package/src/query.ts +612 -0
  63. package/src/queryCache.ts +206 -0
  64. package/src/queryClient.ts +716 -0
  65. package/src/queryObserver.ts +748 -0
  66. package/src/removable.ts +37 -0
  67. package/src/retryer.ts +215 -0
  68. package/src/subscribable.ts +33 -0
  69. package/src/tests/focusManager.test.tsx +155 -0
  70. package/src/tests/hydration.test.tsx +429 -0
  71. package/src/tests/infiniteQueryBehavior.test.tsx +124 -0
  72. package/src/tests/infiniteQueryObserver.test.tsx +64 -0
  73. package/src/tests/mutationCache.test.tsx +260 -0
  74. package/src/tests/mutationObserver.test.tsx +75 -0
  75. package/src/tests/mutations.test.tsx +363 -0
  76. package/src/tests/notifyManager.test.tsx +51 -0
  77. package/src/tests/onlineManager.test.tsx +148 -0
  78. package/src/tests/queriesObserver.test.tsx +330 -0
  79. package/src/tests/query.test.tsx +888 -0
  80. package/src/tests/queryCache.test.tsx +236 -0
  81. package/src/tests/queryClient.test.tsx +1435 -0
  82. package/src/tests/queryObserver.test.tsx +802 -0
  83. package/src/tests/utils.test.tsx +360 -0
  84. package/src/types.ts +705 -0
  85. package/src/utils.ts +435 -0
@@ -0,0 +1,89 @@
1
+ import { Subscribable } from './subscribable'
2
+ import { isServer } from './utils'
3
+
4
+ type SetupFn = (
5
+ setFocused: (focused?: boolean) => void,
6
+ ) => (() => void) | undefined
7
+
8
+ export class FocusManager extends Subscribable {
9
+ private focused?: boolean
10
+ private cleanup?: () => void
11
+
12
+ private setup: SetupFn
13
+
14
+ constructor() {
15
+ super()
16
+ this.setup = (onFocus) => {
17
+ // addEventListener does not exist in React Native, but window does
18
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
19
+ if (!isServer && window.addEventListener) {
20
+ const listener = () => onFocus()
21
+ // Listen to visibillitychange and focus
22
+ window.addEventListener('visibilitychange', listener, false)
23
+ window.addEventListener('focus', listener, false)
24
+
25
+ return () => {
26
+ // Be sure to unsubscribe if a new handler is set
27
+ window.removeEventListener('visibilitychange', listener)
28
+ window.removeEventListener('focus', listener)
29
+ }
30
+ }
31
+ }
32
+ }
33
+
34
+ protected onSubscribe(): void {
35
+ if (!this.cleanup) {
36
+ this.setEventListener(this.setup)
37
+ }
38
+ }
39
+
40
+ protected onUnsubscribe() {
41
+ if (!this.hasListeners()) {
42
+ this.cleanup?.()
43
+ this.cleanup = undefined
44
+ }
45
+ }
46
+
47
+ setEventListener(setup: SetupFn): void {
48
+ this.setup = setup
49
+ this.cleanup?.()
50
+ this.cleanup = setup((focused) => {
51
+ if (typeof focused === 'boolean') {
52
+ this.setFocused(focused)
53
+ } else {
54
+ this.onFocus()
55
+ }
56
+ })
57
+ }
58
+
59
+ setFocused(focused?: boolean): void {
60
+ this.focused = focused
61
+
62
+ if (focused) {
63
+ this.onFocus()
64
+ }
65
+ }
66
+
67
+ onFocus(): void {
68
+ this.listeners.forEach((listener) => {
69
+ listener()
70
+ })
71
+ }
72
+
73
+ isFocused(): boolean {
74
+ if (typeof this.focused === 'boolean') {
75
+ return this.focused
76
+ }
77
+
78
+ // document global can be unavailable in react native
79
+ if (typeof document === 'undefined') {
80
+ return true
81
+ }
82
+
83
+ return [undefined, 'visible', 'prerender'].includes(
84
+ document.visibilityState,
85
+ )
86
+ }
87
+ }
88
+
89
+ export const focusManager = new FocusManager()
@@ -0,0 +1,164 @@
1
+ import type { QueryClient } from './queryClient'
2
+ import type { Query, QueryState } from './query'
3
+ import type {
4
+ MutationKey,
5
+ MutationOptions,
6
+ QueryKey,
7
+ QueryOptions,
8
+ } from './types'
9
+ import type { Mutation, MutationState } from './mutation'
10
+
11
+ // TYPES
12
+
13
+ export interface DehydrateOptions {
14
+ dehydrateMutations?: boolean
15
+ dehydrateQueries?: boolean
16
+ shouldDehydrateMutation?: ShouldDehydrateMutationFunction
17
+ shouldDehydrateQuery?: ShouldDehydrateQueryFunction
18
+ }
19
+
20
+ export interface HydrateOptions {
21
+ defaultOptions?: {
22
+ queries?: QueryOptions
23
+ mutations?: MutationOptions
24
+ }
25
+ }
26
+
27
+ interface DehydratedMutation {
28
+ mutationKey?: MutationKey
29
+ state: MutationState
30
+ }
31
+
32
+ interface DehydratedQuery {
33
+ queryHash: string
34
+ queryKey: QueryKey
35
+ state: QueryState
36
+ }
37
+
38
+ export interface DehydratedState {
39
+ mutations: DehydratedMutation[]
40
+ queries: DehydratedQuery[]
41
+ }
42
+
43
+ export type ShouldDehydrateQueryFunction = (query: Query) => boolean
44
+
45
+ export type ShouldDehydrateMutationFunction = (mutation: Mutation) => boolean
46
+
47
+ // FUNCTIONS
48
+
49
+ function dehydrateMutation(mutation: Mutation): DehydratedMutation {
50
+ return {
51
+ mutationKey: mutation.options.mutationKey,
52
+ state: mutation.state,
53
+ }
54
+ }
55
+
56
+ // Most config is not dehydrated but instead meant to configure again when
57
+ // consuming the de/rehydrated data, typically with useQuery on the client.
58
+ // Sometimes it might make sense to prefetch data on the server and include
59
+ // in the html-payload, but not consume it on the initial render.
60
+ function dehydrateQuery(query: Query): DehydratedQuery {
61
+ return {
62
+ state: query.state,
63
+ queryKey: query.queryKey,
64
+ queryHash: query.queryHash,
65
+ }
66
+ }
67
+
68
+ function defaultShouldDehydrateMutation(mutation: Mutation) {
69
+ return mutation.state.isPaused
70
+ }
71
+
72
+ function defaultShouldDehydrateQuery(query: Query) {
73
+ return query.state.status === 'success'
74
+ }
75
+
76
+ export function dehydrate(
77
+ client: QueryClient,
78
+ options: DehydrateOptions = {},
79
+ ): DehydratedState {
80
+ const mutations: DehydratedMutation[] = []
81
+ const queries: DehydratedQuery[] = []
82
+
83
+ if (options.dehydrateMutations !== false) {
84
+ const shouldDehydrateMutation =
85
+ options.shouldDehydrateMutation || defaultShouldDehydrateMutation
86
+
87
+ client
88
+ .getMutationCache()
89
+ .getAll()
90
+ .forEach((mutation) => {
91
+ if (shouldDehydrateMutation(mutation)) {
92
+ mutations.push(dehydrateMutation(mutation))
93
+ }
94
+ })
95
+ }
96
+
97
+ if (options.dehydrateQueries !== false) {
98
+ const shouldDehydrateQuery =
99
+ options.shouldDehydrateQuery || defaultShouldDehydrateQuery
100
+
101
+ client
102
+ .getQueryCache()
103
+ .getAll()
104
+ .forEach((query) => {
105
+ if (shouldDehydrateQuery(query)) {
106
+ queries.push(dehydrateQuery(query))
107
+ }
108
+ })
109
+ }
110
+
111
+ return { mutations, queries }
112
+ }
113
+
114
+ export function hydrate(
115
+ client: QueryClient,
116
+ dehydratedState: unknown,
117
+ options?: HydrateOptions,
118
+ ): void {
119
+ if (typeof dehydratedState !== 'object' || dehydratedState === null) {
120
+ return
121
+ }
122
+
123
+ const mutationCache = client.getMutationCache()
124
+ const queryCache = client.getQueryCache()
125
+
126
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
127
+ const mutations = (dehydratedState as DehydratedState).mutations || []
128
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
129
+ const queries = (dehydratedState as DehydratedState).queries || []
130
+
131
+ mutations.forEach((dehydratedMutation) => {
132
+ mutationCache.build(
133
+ client,
134
+ {
135
+ ...options?.defaultOptions?.mutations,
136
+ mutationKey: dehydratedMutation.mutationKey,
137
+ },
138
+ dehydratedMutation.state,
139
+ )
140
+ })
141
+
142
+ queries.forEach((dehydratedQuery) => {
143
+ const query = queryCache.get(dehydratedQuery.queryHash)
144
+
145
+ // Do not hydrate if an existing query exists with newer data
146
+ if (query) {
147
+ if (query.state.dataUpdatedAt < dehydratedQuery.state.dataUpdatedAt) {
148
+ query.setState(dehydratedQuery.state)
149
+ }
150
+ return
151
+ }
152
+
153
+ // Restore query
154
+ queryCache.build(
155
+ client,
156
+ {
157
+ ...options?.defaultOptions?.queries,
158
+ queryKey: dehydratedQuery.queryKey,
159
+ queryHash: dehydratedQuery.queryHash,
160
+ },
161
+ dehydratedQuery.state,
162
+ )
163
+ })
164
+ }
package/src/index.ts ADDED
@@ -0,0 +1,35 @@
1
+ export { CancelledError } from './retryer'
2
+ export { QueryCache } from './queryCache'
3
+ export { QueryClient } from './queryClient'
4
+ export { QueryObserver } from './queryObserver'
5
+ export { QueriesObserver } from './queriesObserver'
6
+ export { InfiniteQueryObserver } from './infiniteQueryObserver'
7
+ export { MutationCache } from './mutationCache'
8
+ export { MutationObserver } from './mutationObserver'
9
+ export { notifyManager } from './notifyManager'
10
+ export { focusManager } from './focusManager'
11
+ export { onlineManager } from './onlineManager'
12
+ export {
13
+ hashQueryKey,
14
+ isError,
15
+ parseQueryArgs,
16
+ parseFilterArgs,
17
+ parseMutationFilterArgs,
18
+ parseMutationArgs,
19
+ } from './utils'
20
+ export type { MutationFilters, QueryFilters } from './utils'
21
+ export { isCancelledError } from './retryer'
22
+ export { dehydrate, hydrate } from './hydration'
23
+
24
+ // Types
25
+ export * from './types'
26
+ export type { Query } from './query'
27
+ export type { Mutation } from './mutation'
28
+ export type { Logger } from './logger'
29
+ export type {
30
+ DehydrateOptions,
31
+ DehydratedState,
32
+ HydrateOptions,
33
+ ShouldDehydrateMutationFunction,
34
+ ShouldDehydrateQueryFunction,
35
+ } from './hydration'
@@ -0,0 +1,214 @@
1
+ import type { QueryBehavior } from './query'
2
+
3
+ import type {
4
+ InfiniteData,
5
+ QueryFunctionContext,
6
+ QueryOptions,
7
+ RefetchQueryFilters,
8
+ } from './types'
9
+
10
+ export function infiniteQueryBehavior<
11
+ TQueryFnData,
12
+ TError,
13
+ TData,
14
+ >(): QueryBehavior<TQueryFnData, TError, InfiniteData<TData>> {
15
+ return {
16
+ onFetch: (context) => {
17
+ context.fetchFn = () => {
18
+ const refetchPage: RefetchQueryFilters['refetchPage'] | undefined =
19
+ context.fetchOptions?.meta?.refetchPage
20
+ const fetchMore = context.fetchOptions?.meta?.fetchMore
21
+ const pageParam = fetchMore?.pageParam
22
+ const isFetchingNextPage = fetchMore?.direction === 'forward'
23
+ const isFetchingPreviousPage = fetchMore?.direction === 'backward'
24
+ const oldPages = context.state.data?.pages || []
25
+ const oldPageParams = context.state.data?.pageParams || []
26
+ let newPageParams = oldPageParams
27
+ let cancelled = false
28
+
29
+ const addSignalProperty = (object: unknown) => {
30
+ Object.defineProperty(object, 'signal', {
31
+ enumerable: true,
32
+ get: () => {
33
+ if (context.signal?.aborted) {
34
+ cancelled = true
35
+ } else {
36
+ context.signal?.addEventListener('abort', () => {
37
+ cancelled = true
38
+ })
39
+ }
40
+ return context.signal
41
+ },
42
+ })
43
+ }
44
+
45
+ // Get query function
46
+ const queryFn =
47
+ context.options.queryFn || (() => Promise.reject('Missing queryFn'))
48
+
49
+ const buildNewPages = (
50
+ pages: unknown[],
51
+ param: unknown,
52
+ page: unknown,
53
+ previous?: boolean,
54
+ ) => {
55
+ newPageParams = previous
56
+ ? [param, ...newPageParams]
57
+ : [...newPageParams, param]
58
+ return previous ? [page, ...pages] : [...pages, page]
59
+ }
60
+
61
+ // Create function to fetch a page
62
+ const fetchPage = (
63
+ pages: unknown[],
64
+ manual?: boolean,
65
+ param?: unknown,
66
+ previous?: boolean,
67
+ ): Promise<unknown[]> => {
68
+ if (cancelled) {
69
+ return Promise.reject('Cancelled')
70
+ }
71
+
72
+ if (typeof param === 'undefined' && !manual && pages.length) {
73
+ return Promise.resolve(pages)
74
+ }
75
+
76
+ const queryFnContext: QueryFunctionContext = {
77
+ queryKey: context.queryKey,
78
+ pageParam: param,
79
+ meta: context.meta,
80
+ }
81
+
82
+ addSignalProperty(queryFnContext)
83
+
84
+ const queryFnResult = queryFn(queryFnContext)
85
+
86
+ const promise = Promise.resolve(queryFnResult).then((page) =>
87
+ buildNewPages(pages, param, page, previous),
88
+ )
89
+
90
+ return promise
91
+ }
92
+
93
+ let promise: Promise<unknown[]>
94
+
95
+ // Fetch first page?
96
+ if (!oldPages.length) {
97
+ promise = fetchPage([])
98
+ }
99
+
100
+ // Fetch next page?
101
+ else if (isFetchingNextPage) {
102
+ const manual = typeof pageParam !== 'undefined'
103
+ const param = manual
104
+ ? pageParam
105
+ : getNextPageParam(context.options, oldPages)
106
+ promise = fetchPage(oldPages, manual, param)
107
+ }
108
+
109
+ // Fetch previous page?
110
+ else if (isFetchingPreviousPage) {
111
+ const manual = typeof pageParam !== 'undefined'
112
+ const param = manual
113
+ ? pageParam
114
+ : getPreviousPageParam(context.options, oldPages)
115
+ promise = fetchPage(oldPages, manual, param, true)
116
+ }
117
+
118
+ // Refetch pages
119
+ else {
120
+ newPageParams = []
121
+
122
+ const manual = typeof context.options.getNextPageParam === 'undefined'
123
+
124
+ const shouldFetchFirstPage =
125
+ refetchPage && oldPages[0]
126
+ ? refetchPage(oldPages[0], 0, oldPages)
127
+ : true
128
+
129
+ // Fetch first page
130
+ promise = shouldFetchFirstPage
131
+ ? fetchPage([], manual, oldPageParams[0])
132
+ : Promise.resolve(buildNewPages([], oldPageParams[0], oldPages[0]))
133
+
134
+ // Fetch remaining pages
135
+ for (let i = 1; i < oldPages.length; i++) {
136
+ promise = promise.then((pages) => {
137
+ const shouldFetchNextPage =
138
+ refetchPage && oldPages[i]
139
+ ? refetchPage(oldPages[i], i, oldPages)
140
+ : true
141
+
142
+ if (shouldFetchNextPage) {
143
+ const param = manual
144
+ ? oldPageParams[i]
145
+ : getNextPageParam(context.options, pages)
146
+ return fetchPage(pages, manual, param)
147
+ }
148
+ return Promise.resolve(
149
+ buildNewPages(pages, oldPageParams[i], oldPages[i]),
150
+ )
151
+ })
152
+ }
153
+ }
154
+
155
+ const finalPromise = promise.then((pages) => ({
156
+ pages,
157
+ pageParams: newPageParams,
158
+ }))
159
+
160
+ return finalPromise
161
+ }
162
+ },
163
+ }
164
+ }
165
+
166
+ export function getNextPageParam(
167
+ options: QueryOptions<any, any>,
168
+ pages: unknown[],
169
+ ): unknown | undefined {
170
+ return options.getNextPageParam?.(pages[pages.length - 1], pages)
171
+ }
172
+
173
+ export function getPreviousPageParam(
174
+ options: QueryOptions<any, any>,
175
+ pages: unknown[],
176
+ ): unknown | undefined {
177
+ return options.getPreviousPageParam?.(pages[0], pages)
178
+ }
179
+
180
+ /**
181
+ * Checks if there is a next page.
182
+ * Returns `undefined` if it cannot be determined.
183
+ */
184
+ export function hasNextPage(
185
+ options: QueryOptions<any, any, any, any>,
186
+ pages?: unknown,
187
+ ): boolean | undefined {
188
+ if (options.getNextPageParam && Array.isArray(pages)) {
189
+ const nextPageParam = getNextPageParam(options, pages)
190
+ return (
191
+ typeof nextPageParam !== 'undefined' &&
192
+ nextPageParam !== null &&
193
+ nextPageParam !== false
194
+ )
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Checks if there is a previous page.
200
+ * Returns `undefined` if it cannot be determined.
201
+ */
202
+ export function hasPreviousPage(
203
+ options: QueryOptions<any, any, any, any>,
204
+ pages?: unknown,
205
+ ): boolean | undefined {
206
+ if (options.getPreviousPageParam && Array.isArray(pages)) {
207
+ const previousPageParam = getPreviousPageParam(options, pages)
208
+ return (
209
+ typeof previousPageParam !== 'undefined' &&
210
+ previousPageParam !== null &&
211
+ previousPageParam !== false
212
+ )
213
+ }
214
+ }
@@ -0,0 +1,159 @@
1
+ import type {
2
+ DefaultedInfiniteQueryObserverOptions,
3
+ FetchNextPageOptions,
4
+ FetchPreviousPageOptions,
5
+ InfiniteData,
6
+ InfiniteQueryObserverOptions,
7
+ InfiniteQueryObserverResult,
8
+ QueryKey,
9
+ } from './types'
10
+ import type { QueryClient } from './queryClient'
11
+ import {
12
+ NotifyOptions,
13
+ ObserverFetchOptions,
14
+ QueryObserver,
15
+ } from './queryObserver'
16
+ import {
17
+ hasNextPage,
18
+ hasPreviousPage,
19
+ infiniteQueryBehavior,
20
+ } from './infiniteQueryBehavior'
21
+ import { Query } from './query'
22
+
23
+ type InfiniteQueryObserverListener<TData, TError> = (
24
+ result: InfiniteQueryObserverResult<TData, TError>,
25
+ ) => void
26
+
27
+ export class InfiniteQueryObserver<
28
+ TQueryFnData = unknown,
29
+ TError = unknown,
30
+ TData = TQueryFnData,
31
+ TQueryData = TQueryFnData,
32
+ TQueryKey extends QueryKey = QueryKey,
33
+ > extends QueryObserver<
34
+ TQueryFnData,
35
+ TError,
36
+ InfiniteData<TData>,
37
+ InfiniteData<TQueryData>,
38
+ TQueryKey
39
+ > {
40
+ // Type override
41
+ subscribe!: (
42
+ listener?: InfiniteQueryObserverListener<TData, TError>,
43
+ ) => () => void
44
+
45
+ // Type override
46
+ getCurrentResult!: () => InfiniteQueryObserverResult<TData, TError>
47
+
48
+ // Type override
49
+ protected fetch!: (
50
+ fetchOptions: ObserverFetchOptions,
51
+ ) => Promise<InfiniteQueryObserverResult<TData, TError>>
52
+
53
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
54
+ constructor(
55
+ client: QueryClient,
56
+ options: InfiniteQueryObserverOptions<
57
+ TQueryFnData,
58
+ TError,
59
+ TData,
60
+ TQueryData,
61
+ TQueryKey
62
+ >,
63
+ ) {
64
+ super(client, options)
65
+ }
66
+
67
+ protected bindMethods(): void {
68
+ super.bindMethods()
69
+ this.fetchNextPage = this.fetchNextPage.bind(this)
70
+ this.fetchPreviousPage = this.fetchPreviousPage.bind(this)
71
+ }
72
+
73
+ setOptions(
74
+ options?: InfiniteQueryObserverOptions<
75
+ TQueryFnData,
76
+ TError,
77
+ TData,
78
+ TQueryData,
79
+ TQueryKey
80
+ >,
81
+ notifyOptions?: NotifyOptions,
82
+ ): void {
83
+ super.setOptions(
84
+ {
85
+ ...options,
86
+ behavior: infiniteQueryBehavior(),
87
+ },
88
+ notifyOptions,
89
+ )
90
+ }
91
+
92
+ getOptimisticResult(
93
+ options: DefaultedInfiniteQueryObserverOptions<
94
+ TQueryFnData,
95
+ TError,
96
+ TData,
97
+ TQueryData,
98
+ TQueryKey
99
+ >,
100
+ ): InfiniteQueryObserverResult<TData, TError> {
101
+ options.behavior = infiniteQueryBehavior()
102
+ return super.getOptimisticResult(options) as InfiniteQueryObserverResult<
103
+ TData,
104
+ TError
105
+ >
106
+ }
107
+
108
+ fetchNextPage({ pageParam, ...options }: FetchNextPageOptions = {}): Promise<
109
+ InfiniteQueryObserverResult<TData, TError>
110
+ > {
111
+ return this.fetch({
112
+ ...options,
113
+ meta: {
114
+ fetchMore: { direction: 'forward', pageParam },
115
+ },
116
+ })
117
+ }
118
+
119
+ fetchPreviousPage({
120
+ pageParam,
121
+ ...options
122
+ }: FetchPreviousPageOptions = {}): Promise<
123
+ InfiniteQueryObserverResult<TData, TError>
124
+ > {
125
+ return this.fetch({
126
+ ...options,
127
+ meta: {
128
+ fetchMore: { direction: 'backward', pageParam },
129
+ },
130
+ })
131
+ }
132
+
133
+ protected createResult(
134
+ query: Query<TQueryFnData, TError, InfiniteData<TQueryData>, TQueryKey>,
135
+ options: InfiniteQueryObserverOptions<
136
+ TQueryFnData,
137
+ TError,
138
+ TData,
139
+ TQueryData,
140
+ TQueryKey
141
+ >,
142
+ ): InfiniteQueryObserverResult<TData, TError> {
143
+ const { state } = query
144
+ const result = super.createResult(query, options)
145
+ return {
146
+ ...result,
147
+ fetchNextPage: this.fetchNextPage,
148
+ fetchPreviousPage: this.fetchPreviousPage,
149
+ hasNextPage: hasNextPage(options, state.data?.pages),
150
+ hasPreviousPage: hasPreviousPage(options, state.data?.pages),
151
+ isFetchingNextPage:
152
+ state.fetchStatus === 'fetching' &&
153
+ state.fetchMeta?.fetchMore?.direction === 'forward',
154
+ isFetchingPreviousPage:
155
+ state.fetchStatus === 'fetching' &&
156
+ state.fetchMeta?.fetchMore?.direction === 'backward',
157
+ }
158
+ }
159
+ }
@@ -0,0 +1,11 @@
1
+ import type { Logger } from './logger'
2
+
3
+ /**
4
+ * See https://github.com/tannerlinsley/react-query/issues/795
5
+ * and https://github.com/tannerlinsley/react-query/pull/3246/#discussion_r795105707
6
+ */
7
+ export const defaultLogger: Logger = {
8
+ log: console.log,
9
+ warn: console.warn,
10
+ error: console.warn,
11
+ }
package/src/logger.ts ADDED
@@ -0,0 +1,9 @@
1
+ export interface Logger {
2
+ log: LogFunction
3
+ warn: LogFunction
4
+ error: LogFunction
5
+ }
6
+
7
+ type LogFunction = (...args: any[]) => void
8
+
9
+ export const defaultLogger: Logger = console