Skip to content

Commit 7cd1af1

Browse files
committed
refactor: Move infinite query logic into queryCache
1 parent 5a5ad74 commit 7cd1af1

File tree

6 files changed

+155
-156
lines changed

6 files changed

+155
-156
lines changed

media/logo.sketch

-2.49 KB
Binary file not shown.

src/core/queryCache.js

Lines changed: 123 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
statusIdle,
1414
Console,
1515
isObject,
16+
getStatusBools,
1617
} from './utils'
1718

1819
import { defaultConfigRef } from './config'
@@ -153,6 +154,23 @@ export function makeQueryCache({ frozen = isServer, defaultConfig } = {}) {
153154
config,
154155
})
155156

157+
if (config.infinite) {
158+
if (
159+
typeof query.state.canFetchMore === 'undefined' &&
160+
typeof query.state.data !== 'undefined'
161+
) {
162+
query.state.canFetchMore = config.getFetchMore(
163+
query.state.data[query.state.data.length - 1],
164+
query.state.data
165+
)
166+
}
167+
168+
// Here we seed the pageVariabes for the query
169+
if (!query.pageVariables) {
170+
query.pageVariables = [[...query.queryKey]]
171+
}
172+
}
173+
156174
// If the query started with data, schedule
157175
// a stale timeout
158176
if (!isServer && query.state.data) {
@@ -310,6 +328,14 @@ export function makeQueryCache({ frozen = isServer, defaultConfig } = {}) {
310328
)
311329
}
312330

331+
query.refetch = async () => {
332+
try {
333+
await query.fetch()
334+
} catch (error) {
335+
Console.error(error)
336+
}
337+
}
338+
313339
query.heal = () => {
314340
// Stop the query from being garbage collected
315341
clearTimeout(query.cacheTimeout)
@@ -507,10 +533,98 @@ export function makeQueryCache({ frozen = isServer, defaultConfig } = {}) {
507533
}
508534
}
509535

510-
query.fetch = async ({ queryFn = query.config.queryFn } = {}) => {
536+
query.fetch = async ({ fetchMore } = {}) => {
537+
let queryFn = query.config.queryFn
538+
511539
if (!queryFn) {
512540
return
513541
}
542+
543+
if (query.config.infinite) {
544+
const originalQueryFn = queryFn
545+
546+
queryFn = async () => {
547+
const data = []
548+
const pageVariables = [...query.pageVariables]
549+
const rebuiltPageVariables = []
550+
551+
do {
552+
const args = pageVariables.shift()
553+
554+
if (!data.length) {
555+
// the first page query doesn't need to be rebuilt
556+
data.push(await originalQueryFn(...args))
557+
rebuiltPageVariables.push(args)
558+
} else {
559+
// get an up-to-date cursor based on the previous data set
560+
561+
const nextCursor = query.config.getFetchMore(
562+
data[data.length - 1],
563+
data
564+
)
565+
566+
// break early if there's no next cursor
567+
// otherwise we'll start from the beginning
568+
// which will cause unwanted duplication
569+
if (!nextCursor) {
570+
break
571+
}
572+
573+
const pageArgs = [
574+
// remove the last argument (the previously saved cursor)
575+
...args.slice(0, -1),
576+
nextCursor,
577+
]
578+
579+
data.push(await originalQueryFn(...pageArgs))
580+
rebuiltPageVariables.push(pageArgs)
581+
}
582+
} while (pageVariables.length)
583+
584+
query.state.canFetchMore = query.config.getFetchMore(
585+
data[data.length - 1],
586+
data
587+
)
588+
query.pageVariables = rebuiltPageVariables
589+
590+
return data
591+
}
592+
593+
if (fetchMore) {
594+
queryFn = async (...args) => {
595+
const { fetchMoreInfo, previous } = fetchMore
596+
try {
597+
query.setState(old => ({
598+
...old,
599+
isFetchingMore: previous ? 'previous' : 'next',
600+
}))
601+
602+
const newArgs = [...args, fetchMoreInfo]
603+
604+
query.pageVariables[previous ? 'unshift' : 'push'](newArgs)
605+
606+
const newData = await originalQueryFn(...newArgs)
607+
608+
const data = previous
609+
? [newData, ...query.state.data]
610+
: [...query.state.data, newData]
611+
612+
query.state.canFetchMore = query.config.getFetchMore(
613+
newData,
614+
data
615+
)
616+
617+
return data
618+
} finally {
619+
query.setState(old => ({
620+
...old,
621+
isFetchingMore: false,
622+
}))
623+
}
624+
}
625+
}
626+
}
627+
514628
// Create a new promise for the query cache if necessary
515629
if (!query.promise) {
516630
query.promise = (async () => {
@@ -582,6 +696,13 @@ export function makeQueryCache({ frozen = isServer, defaultConfig } = {}) {
582696
return query.promise
583697
}
584698

699+
if (query.config.infinite) {
700+
query.fetchMore = (
701+
fetchMoreInfo = query.state.canFetchMore,
702+
{ previous = false } = {}
703+
) => query.fetch({ fetchMore: { fetchMoreInfo, previous } })
704+
}
705+
585706
return query
586707
}
587708

@@ -591,14 +712,7 @@ export function makeQueryCache({ frozen = isServer, defaultConfig } = {}) {
591712
export function queryReducer(state, action) {
592713
const newState = switchActions(state, action)
593714

594-
Object.assign(newState, {
595-
isLoading: newState.status === statusLoading,
596-
isSuccess: newState.status === statusSuccess,
597-
isError: newState.status === statusError,
598-
isIdle: newState.status === statusIdle,
599-
})
600-
601-
return newState
715+
return Object.assign(newState, getStatusBools(newState.status))
602716
}
603717

604718
function switchActions(state, action) {
@@ -608,7 +722,6 @@ function switchActions(state, action) {
608722
status: action.initialStatus,
609723
error: null,
610724
isFetching: action.initialStatus === 'loading',
611-
canFetchMore: false,
612725
failureCount: 0,
613726
isStale: action.isStale,
614727
markedForGarbageCollection: false,
@@ -647,7 +760,6 @@ function switchActions(state, action) {
647760
error: null,
648761
isStale: false,
649762
isFetching: false,
650-
canFetchMore: action.canFetchMore,
651763
updatedAt: Date.now(),
652764
failureCount: 0,
653765
}

src/core/utils.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,12 @@ export function deepEqual(a, b) {
128128
// eslint-disable-next-line no-self-compare
129129
return a !== a && b !== b
130130
}
131+
132+
export function getStatusBools(status) {
133+
return {
134+
isLoading: status === statusLoading,
135+
isSuccess: status === statusSuccess,
136+
isError: status === statusError,
137+
isIdle: status === statusIdle,
138+
}
139+
}

src/react/useBaseQuery.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import React from 'react'
44

55
import { useQueryCache } from './ReactQueryCacheProvider'
66
import { useMountedCallback } from './utils'
7-
import { Console } from '../core/utils'
87

98
export function useBaseQuery(queryKey, config = {}) {
109
// Make a rerender function
@@ -40,17 +39,9 @@ export function useBaseQuery(queryKey, config = {}) {
4039
instanceRef.current.run()
4140
}, [config.enabled, query])
4241

43-
const refetch = React.useCallback(async () => {
44-
try {
45-
await query.fetch()
46-
} catch (error) {
47-
Console.error(error)
48-
}
49-
}, [query])
50-
5142
return {
52-
query,
53-
refetch,
43+
...query,
5444
...query.state,
45+
query,
5546
}
5647
}

src/react/useInfiniteQuery.js

Lines changed: 3 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,16 @@
1-
import React from 'react'
2-
31
//
42

53
import { useBaseQuery } from './useBaseQuery'
6-
import { useQueryArgs, useGetLatest, handleSuspense } from './utils'
4+
import { useQueryArgs, handleSuspense } from './utils'
75

86
export function useInfiniteQuery(...args) {
9-
const queryInfoRef = React.useRef()
107
let [queryKey, config] = useQueryArgs(args)
118

12-
const { getFetchMore } = config
13-
const getGetFetchMore = useGetLatest(getFetchMore)
14-
15-
// The default queryFn will query all pages and map them together
16-
const originalQueryFn = config.queryFn
17-
18-
config.queryFn = async () => {
19-
const data = []
20-
const pageVariables = [...queryInfoRef.current.query.pageVariables]
21-
const rebuiltPageVariables = []
22-
23-
do {
24-
const args = pageVariables.shift()
25-
26-
if (!data.length) {
27-
// the first page query doesn't need to be rebuilt
28-
data.push(await originalQueryFn(...args))
29-
rebuiltPageVariables.push(args)
30-
} else {
31-
// get an up-to-date cursor based on the previous data set
32-
const nextCursor = getGetFetchMore()(data[data.length - 1], data)
33-
34-
// break early if there's no next cursor
35-
// otherwise we'll start from the beginning
36-
// which will cause unwanted duplication
37-
if (!nextCursor) {
38-
break
39-
}
40-
41-
const pageArgs = [
42-
// remove the last argument (the previously saved cursor)
43-
...args.slice(0, -1),
44-
nextCursor,
45-
]
46-
47-
data.push(await originalQueryFn(...pageArgs))
48-
rebuiltPageVariables.push(pageArgs)
49-
}
50-
} while (pageVariables.length)
51-
52-
queryInfoRef.current.query.canFetchMore = getGetFetchMore()(
53-
data[data.length - 1],
54-
data
55-
)
56-
queryInfoRef.current.query.pageVariables = rebuiltPageVariables
57-
58-
return data
59-
}
9+
config.infinite = true
6010

6111
const queryInfo = useBaseQuery(queryKey, config)
6212

63-
if (
64-
typeof queryInfo.query.canFetchMore === 'undefined' &&
65-
typeof queryInfo.data !== 'undefined'
66-
) {
67-
queryInfo.query.canFetchMore = getGetFetchMore()(
68-
queryInfo.data[queryInfo.data.length - 1],
69-
queryInfo.data
70-
)
71-
}
72-
73-
queryInfoRef.current = queryInfo
74-
75-
let {
76-
data = [],
77-
query: { canFetchMore },
78-
} = queryInfo
79-
80-
// Here we seed the pageVariabes for the query
81-
if (!queryInfo.query.pageVariables) {
82-
queryInfo.query.pageVariables = [[...queryInfo.query.queryKey]]
83-
}
84-
85-
const fetchMore = React.useCallback(
86-
(
87-
fetchMoreInfo = queryInfoRef.current.query.canFetchMore,
88-
{ previous = false } = {}
89-
) =>
90-
queryInfoRef.current.query.canFetchMore
91-
? queryInfoRef.current.query.fetch({
92-
queryFn: async (...args) => {
93-
try {
94-
queryInfoRef.current.query.setState(old => ({
95-
...old,
96-
isFetchingMore: previous ? 'previous' : 'next',
97-
}))
98-
99-
const newArgs = previous
100-
? [fetchMoreInfo, ...args]
101-
: [...args, fetchMoreInfo]
102-
queryInfoRef.current.query.pageVariables[
103-
previous ? 'unshift' : 'push'
104-
](newArgs)
105-
106-
const newData = await originalQueryFn(...newArgs)
107-
108-
const data = previous
109-
? [newData, ...queryInfoRef.current.data]
110-
: [...queryInfoRef.current.data, newData]
111-
112-
queryInfoRef.current.query.canFetchMore = getGetFetchMore()(
113-
newData,
114-
data
115-
)
116-
117-
return data
118-
} finally {
119-
queryInfoRef.current.query.setState(old => ({
120-
...old,
121-
isFetchingMore: false,
122-
}))
123-
}
124-
},
125-
})
126-
: void 0,
127-
[getGetFetchMore, originalQueryFn]
128-
)
129-
13013
handleSuspense(queryInfo)
13114

132-
return {
133-
...queryInfo,
134-
data,
135-
canFetchMore,
136-
fetchMore,
137-
}
15+
return queryInfo
13816
}

0 commit comments

Comments
 (0)