@@ -2,16 +2,30 @@ import type { DocumentNode } from "graphql";
2
2
import type { TypedDocumentNode } from "@graphql-typed-document-node/core" ;
3
3
import * as React from "rehackt" ;
4
4
5
- import type { OperationVariables } from "../../core/index.js" ;
5
+ import type {
6
+ ApolloClient ,
7
+ ApolloQueryResult ,
8
+ OperationVariables ,
9
+ WatchQueryOptions ,
10
+ } from "../../core/index.js" ;
6
11
import { mergeOptions } from "../../utilities/index.js" ;
7
12
import type {
8
13
LazyQueryHookExecOptions ,
9
14
LazyQueryHookOptions ,
10
15
LazyQueryResultTuple ,
11
16
NoInfer ,
17
+ QueryHookOptions ,
18
+ QueryResult ,
12
19
} from "../types/types.js" ;
13
- import { useInternalState } from "./useQuery.js" ;
14
- import { useApolloClient } from "./useApolloClient.js" ;
20
+ import type { InternalResult , ObsQueryWithMeta } from "./useQuery.js" ;
21
+ import {
22
+ createMakeWatchQueryOptions ,
23
+ getDefaultFetchPolicy ,
24
+ getObsQueryOptions ,
25
+ toQueryResult ,
26
+ useQueryInternals ,
27
+ } from "./useQuery.js" ;
28
+ import { useIsomorphicLayoutEffect } from "./internal/useIsomorphicLayoutEffect.js" ;
15
29
16
30
// The following methods, when called will execute the query, regardless of
17
31
// whether the useLazyQuery execute function was called before.
@@ -21,6 +35,7 @@ const EAGER_METHODS = [
21
35
"fetchMore" ,
22
36
"updateQuery" ,
23
37
"startPolling" ,
38
+ "stopPolling" ,
24
39
"subscribeToMore" ,
25
40
] as const ;
26
41
@@ -80,21 +95,27 @@ export function useLazyQuery<
80
95
optionsRef . current = options ;
81
96
queryRef . current = document ;
82
97
83
- const internalState = useInternalState < TData , TVariables > (
84
- useApolloClient ( options && options . client ) ,
85
- document
86
- ) ;
87
-
88
- const useQueryResult = internalState . useQuery ( {
98
+ const queryHookOptions = {
89
99
...merged ,
90
100
skip : ! execOptionsRef . current ,
91
- } ) ;
101
+ } ;
102
+ const {
103
+ obsQueryFields,
104
+ result : useQueryResult ,
105
+ client,
106
+ resultData,
107
+ observable,
108
+ onQueryExecuted,
109
+ } = useQueryInternals ( document , queryHookOptions ) ;
92
110
93
111
const initialFetchPolicy =
94
- useQueryResult . observable . options . initialFetchPolicy ||
95
- internalState . getDefaultFetchPolicy ( ) ;
112
+ observable . options . initialFetchPolicy ||
113
+ getDefaultFetchPolicy (
114
+ queryHookOptions . defaultOptions ,
115
+ client . defaultOptions
116
+ ) ;
96
117
97
- const { forceUpdateState, obsQueryFields } = internalState ;
118
+ const forceUpdateState = React . useReducer ( ( tick ) => tick + 1 , 0 ) [ 1 ] ;
98
119
// We use useMemo here to make sure the eager methods have a stable identity.
99
120
const eagerMethods = React . useMemo ( ( ) => {
100
121
const eagerMethods : Record < string , any > = { } ;
@@ -111,7 +132,7 @@ export function useLazyQuery<
111
132
} ;
112
133
}
113
134
114
- return eagerMethods ;
135
+ return eagerMethods as typeof obsQueryFields ;
115
136
} , [ forceUpdateState , obsQueryFields ] ) ;
116
137
117
138
const called = ! ! execOptionsRef . current ;
@@ -141,18 +162,95 @@ export function useLazyQuery<
141
162
...execOptionsRef . current ,
142
163
} ) ;
143
164
144
- const promise = internalState
145
- . executeQuery ( { ...options , skip : false } )
146
- . then ( ( queryResult ) => Object . assign ( queryResult , eagerMethods ) ) ;
165
+ const promise = executeQuery (
166
+ resultData ,
167
+ observable ,
168
+ client ,
169
+ document ,
170
+ { ...options , skip : false } ,
171
+ onQueryExecuted
172
+ ) . then ( ( queryResult ) => Object . assign ( queryResult , eagerMethods ) ) ;
147
173
148
174
// Because the return value of `useLazyQuery` is usually floated, we need
149
175
// to catch the promise to prevent unhandled rejections.
150
176
promise . catch ( ( ) => { } ) ;
151
177
152
178
return promise ;
153
179
} ,
154
- [ eagerMethods , initialFetchPolicy , internalState ]
180
+ [
181
+ client ,
182
+ document ,
183
+ eagerMethods ,
184
+ initialFetchPolicy ,
185
+ observable ,
186
+ resultData ,
187
+ onQueryExecuted ,
188
+ ]
189
+ ) ;
190
+
191
+ const executeRef = React . useRef ( execute ) ;
192
+ useIsomorphicLayoutEffect ( ( ) => {
193
+ executeRef . current = execute ;
194
+ } ) ;
195
+
196
+ const stableExecute = React . useCallback < typeof execute > (
197
+ ( ...args ) => executeRef . current ( ...args ) ,
198
+ [ ]
155
199
) ;
200
+ return [ stableExecute , result ] ;
201
+ }
156
202
157
- return [ execute , result ] ;
203
+ function executeQuery < TData , TVariables extends OperationVariables > (
204
+ resultData : InternalResult < TData , TVariables > ,
205
+ observable : ObsQueryWithMeta < TData , TVariables > ,
206
+ client : ApolloClient < object > ,
207
+ currentQuery : DocumentNode ,
208
+ options : QueryHookOptions < TData , TVariables > & {
209
+ query ?: DocumentNode ;
210
+ } ,
211
+ onQueryExecuted : ( options : WatchQueryOptions < TVariables , TData > ) => void
212
+ ) {
213
+ const query = options . query || currentQuery ;
214
+ const watchQueryOptions = createMakeWatchQueryOptions (
215
+ client ,
216
+ query ,
217
+ options ,
218
+ false
219
+ ) ( observable ) ;
220
+
221
+ const concast = observable . reobserveAsConcast (
222
+ getObsQueryOptions ( observable , client , options , watchQueryOptions )
223
+ ) ;
224
+ onQueryExecuted ( watchQueryOptions ) ;
225
+
226
+ return new Promise <
227
+ Omit < QueryResult < TData , TVariables > , ( typeof EAGER_METHODS ) [ number ] >
228
+ > ( ( resolve ) => {
229
+ let result : ApolloQueryResult < TData > ;
230
+
231
+ // Subscribe to the concast independently of the ObservableQuery in case
232
+ // the component gets unmounted before the promise resolves. This prevents
233
+ // the concast from terminating early and resolving with `undefined` when
234
+ // there are no more subscribers for the concast.
235
+ concast . subscribe ( {
236
+ next : ( value ) => {
237
+ result = value ;
238
+ } ,
239
+ error : ( ) => {
240
+ resolve (
241
+ toQueryResult (
242
+ observable . getCurrentResult ( ) ,
243
+ resultData . previousData ,
244
+ observable ,
245
+ client
246
+ )
247
+ ) ;
248
+ } ,
249
+ complete : ( ) => {
250
+ resolve (
251
+ toQueryResult ( result , resultData . previousData , observable , client )
252
+ ) ;
253
+ } ,
254
+ } ) ;
255
+ } ) ;
158
256
}
0 commit comments