1
1
import { DocumentNode } from 'graphql' ;
2
- import { useCallback , useRef } from 'react' ;
3
- import { pipe , onEnd , subscribe } from 'wonka' ;
2
+ import { useCallback , useMemo } from 'react' ;
3
+ import { pipe , concat , fromValue , switchMap , map , scan } from 'wonka' ;
4
+ import { useSubjectValue } from 'react-wonka' ;
5
+
4
6
import { useClient } from '../context' ;
5
7
import { OperationContext , RequestPolicy } from '../types' ;
6
- import { CombinedError , noop } from '../utils' ;
8
+ import { CombinedError } from '../utils' ;
7
9
import { useRequest } from './useRequest' ;
8
- import { useImmediateEffect } from './useImmediateEffect' ;
9
- import { useImmediateState } from './useImmediateState' ;
10
+
11
+ const initialState : UseQueryState < any > = {
12
+ fetching : false ,
13
+ data : undefined ,
14
+ error : undefined ,
15
+ extensions : undefined ,
16
+ } ;
10
17
11
18
export interface UseQueryArgs < V > {
12
19
query : string | DocumentNode ;
@@ -32,61 +39,60 @@ export type UseQueryResponse<T> = [
32
39
export const useQuery = < T = any , V = object > (
33
40
args : UseQueryArgs < V >
34
41
) : UseQueryResponse < T > => {
35
- const unsubscribe = useRef ( noop ) ;
36
42
const client = useClient ( ) ;
37
43
38
- // This is like useState but updates the state object
39
- // immediately, when we're still before the initial mount
40
- const [ state , setState ] = useImmediateState < UseQueryState < T > > ( {
41
- fetching : false ,
42
- data : undefined ,
43
- error : undefined ,
44
- extensions : undefined ,
45
- } ) ;
46
-
47
44
// This creates a request which will keep a stable reference
48
45
// if request.key doesn't change
49
46
const request = useRequest ( args . query , args . variables ) ;
50
47
51
- const executeQuery = useCallback (
48
+ // Create a new query-source from client.executeQuery
49
+ const makeQuery$ = useCallback (
52
50
( opts ?: Partial < OperationContext > ) => {
53
- unsubscribe . current ( ) ;
51
+ return client . executeQuery ( request , {
52
+ requestPolicy : args . requestPolicy ,
53
+ pollInterval : args . pollInterval ,
54
+ ...args . context ,
55
+ ...opts ,
56
+ } ) ;
57
+ } ,
58
+ [ client , request , args . requestPolicy , args . pollInterval , args . context ]
59
+ ) ;
54
60
55
- setState ( s => ( { ...s , fetching : true } ) ) ;
61
+ const [ state , update ] = useSubjectValue (
62
+ query$$ =>
63
+ pipe (
64
+ query$$ ,
65
+ switchMap ( query$ => {
66
+ if ( ! query$ ) return fromValue ( { fetching : false } ) ;
56
67
57
- [ unsubscribe . current ] = pipe (
58
- client . executeQuery ( request , {
59
- requestPolicy : args . requestPolicy ,
60
- pollInterval : args . pollInterval ,
61
- ...args . context ,
62
- ...opts ,
68
+ return concat ( [
69
+ // Initially set fetching to true
70
+ fromValue ( { fetching : true } ) ,
71
+ pipe (
72
+ query$ ,
73
+ map ( ( { data, error, extensions } ) => ( {
74
+ fetching : false ,
75
+ data,
76
+ error,
77
+ extensions,
78
+ } ) )
79
+ ) ,
80
+ // When the source proactively closes, fetching is set to false
81
+ fromValue ( { fetching : false } ) ,
82
+ ] ) ;
63
83
} ) ,
64
- onEnd ( ( ) => setState ( s => ( { ...s , fetching : false } ) ) ) ,
65
- subscribe ( ( { data, error, extensions } ) => {
66
- setState ( { fetching : false , data, error, extensions } ) ;
67
- } )
68
- ) ;
69
- } ,
70
- [
71
- args . context ,
72
- args . requestPolicy ,
73
- args . pollInterval ,
74
- client ,
75
- request ,
76
- setState ,
77
- ]
84
+ // The individual partial results are merged into each previous result
85
+ scan ( ( result , partial ) => ( { ...result , ...partial } ) , initialState )
86
+ ) ,
87
+ useMemo ( ( ) => ( args . pause ? null : makeQuery$ ( ) ) , [ args . pause , makeQuery$ ] ) ,
88
+ initialState
78
89
) ;
79
90
80
- useImmediateEffect ( ( ) => {
81
- if ( args . pause ) {
82
- unsubscribe . current ( ) ;
83
- setState ( s => ( { ...s , fetching : false } ) ) ;
84
- return noop ;
85
- }
86
-
87
- executeQuery ( ) ;
88
- return ( ) => unsubscribe . current ( ) ; // eslint-disable-line
89
- } , [ executeQuery , args . pause , setState ] ) ;
91
+ // This is the imperative execute function passed to the user
92
+ const executeQuery = useCallback (
93
+ ( opts ?: Partial < OperationContext > ) => update ( makeQuery$ ( opts ) ) ,
94
+ [ update , makeQuery$ ]
95
+ ) ;
90
96
91
97
return [ state , executeQuery ] ;
92
98
} ;
0 commit comments