-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathPrefetchEndpoints.tsx
103 lines (95 loc) · 2.88 KB
/
PrefetchEndpoints.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import { createApi } from '.'
import type { BaseQueryFn } from '../baseQueryTypes'
import type { ApiEndpointQuery } from '../core'
import type { QueryDefinition } from '../endpointDefinitions'
import { fetchBaseQuery } from '../fetchBaseQuery'
import type { EndpointRequest } from './HydrateEndpoints.cc'
// this needs to be a separately bundled entry point prefixed with "use client"
import { HydrateEndpoints } from './HydrateEndpoints.cc'
interface PrefetchEndpointsProps<BaseQuery extends BaseQueryFn> {
baseQuery: BaseQueryFn
run: (
prefetchEndpoint: <QueryArg, ReturnType>(
endpoint: ApiEndpointQuery<
QueryDefinition<QueryArg, BaseQuery, any, ReturnType, any>,
any
>,
arg: QueryArg,
) => Promise<ReturnType>,
) => Promise<void> | undefined
children?: any
}
export function PrefetchEndpoints<BaseQuery extends BaseQueryFn>({
baseQuery,
run,
children,
}: PrefetchEndpointsProps<BaseQuery>) {
const immediateRequests: Array<EndpointRequest> = []
const lateRequests = generateRequests()
async function* generateRequests(): AsyncGenerator<EndpointRequest> {
let resolveNext: undefined | PromiseWithResolvers<EndpointRequest>
const running = run((endpoint, arg) => {
// something something magic
const request = {
serializedQueryArgs: '...',
resolvedAndTransformedData: {}, // ...
} as any as EndpointRequest
if (!resolveNext) {
immediateRequests.push(request)
} else {
const oldResolveNext = resolveNext
resolveNext = Promise.withResolvers()
oldResolveNext.resolve(request)
}
return request.resolvedAndTransformedData
})
// not an async function, no need to wait for late requests
if (!running) return
let runningResolved = false
running.then(() => {
runningResolved = true
})
resolveNext = Promise.withResolvers()
while (!runningResolved) {
yield await resolveNext.promise
}
}
return (
<HydrateEndpoints
immediateRequests={immediateRequests}
lateRequests={lateRequests}
>
{children}
</HydrateEndpoints>
)
}
// usage:
const baseQuery = fetchBaseQuery()
const api = createApi({
baseQuery,
endpoints: (build) => ({
foo: build.query<string, string>({
query(arg) {
return { url: '/foo' + arg }
},
}),
}),
})
function Page() {
return (
<PrefetchEndpoints
baseQuery={baseQuery}
run={async (prefetch) => {
// immediate prefetching
const promise1 = prefetch(api.endpoints.foo, 'bar')
const promise2 = prefetch(api.endpoints.foo, 'baz')
// and a "dependent endpoint" that can only be prefetched with the result of the first two
const result1 = await promise1
const result2 = await promise2
prefetch(api.endpoints.foo, result1 + result2)
}}
>
foo
</PrefetchEndpoints>
)
}