@@ -22,41 +22,21 @@ import {
22
22
QueryKey ,
23
23
UseQueryOptions ,
24
24
UseQueryResult ,
25
+ hashQueryKey ,
25
26
} from "react-query" ;
26
27
import {
27
28
DocumentData ,
28
29
DocumentReference ,
29
30
DocumentSnapshot ,
30
- getDoc ,
31
- getDocFromCache ,
32
- getDocFromServer ,
33
31
onSnapshot ,
34
- SnapshotOptions ,
35
32
Unsubscribe ,
36
33
FirestoreError ,
37
34
} from "firebase/firestore" ;
38
- import {
39
- GetSnapshotSource ,
40
- UseFirestoreHookOptions ,
41
- WithIdField ,
42
- } from "./index" ;
43
-
44
- async function getSnapshot < T > (
45
- ref : DocumentReference < T > ,
46
- source ?: GetSnapshotSource
47
- ) : Promise < DocumentSnapshot < T > > {
48
- let snapshot : DocumentSnapshot < T > ;
49
-
50
- if ( source === "cache" ) {
51
- snapshot = await getDocFromCache ( ref ) ;
52
- } else if ( source === "server" ) {
53
- snapshot = await getDocFromServer ( ref ) ;
54
- } else {
55
- snapshot = await getDoc ( ref ) ;
56
- }
35
+ import { getSnapshot , UseFirestoreHookOptions } from "./index" ;
36
+ import { Completer } from "../../utils/src" ;
57
37
58
- return snapshot ;
59
- }
38
+ const counts : { [ key : string ] : number } = { } ;
39
+ const subscriptions : { [ key : string ] : Unsubscribe } = { } ;
60
40
61
41
export function useFirestoreDocument < T = DocumentData , R = DocumentSnapshot < T > > (
62
42
key : QueryKey ,
@@ -68,152 +48,70 @@ export function useFirestoreDocument<T = DocumentData, R = DocumentSnapshot<T>>(
68
48
>
69
49
) : UseQueryResult < R , FirestoreError > {
70
50
const client = useQueryClient ( ) ;
71
- const unsubscribe = useRef < Unsubscribe > ( ) ;
51
+ const completer = useRef < Completer < DocumentSnapshot < T > > > ( new Completer ( ) ) ;
72
52
73
- useEffect ( ( ) => {
74
- return ( ) => unsubscribe . current ?.( ) ;
75
- } , [ ] ) ;
53
+ const hashFn = useQueryOptions ?. queryKeyHashFn || hashQueryKey ;
54
+ const hash = hashFn ( key ) ;
76
55
77
- return useQuery < DocumentSnapshot < T > , FirestoreError , R > ( {
78
- ...useQueryOptions ,
79
- queryKey : useQueryOptions ?. queryKey ?? key ,
80
- staleTime :
81
- useQueryOptions ?. staleTime ?? options ?. subscribe ? Infinity : undefined ,
82
- async queryFn ( ) {
83
- unsubscribe . current ?.( ) ;
56
+ const isSubscription = ! ! options ?. subscribe ;
84
57
85
- if ( ! options ?. subscribe ) {
86
- return getSnapshot ( ref , options ?. source ) ;
87
- }
58
+ useEffect ( ( ) => {
59
+ if ( ! isSubscription ) {
60
+ getSnapshot ( ref , options ?. source )
61
+ . then ( ( snapshot ) => {
62
+ completer . current ! . complete ( snapshot ) ;
63
+ } )
64
+ . catch ( ( error ) => {
65
+ completer . current ! . reject ( error ) ;
66
+ } ) ;
67
+ }
68
+ } , [ isSubscription , hash , completer ] ) ;
88
69
89
- let resolved = false ;
70
+ useEffect ( ( ) => {
71
+ if ( isSubscription ) {
72
+ counts [ hash ] ??= 0 ;
73
+ counts [ hash ] ++ ;
90
74
91
- return new Promise < DocumentSnapshot < T > > ( ( resolve , reject ) => {
92
- unsubscribe . current = onSnapshot (
75
+ // If there is only one instance of this query key, subscribe
76
+ if ( counts [ hash ] === 1 ) {
77
+ subscriptions [ hash ] = onSnapshot (
93
78
ref ,
94
79
{
95
80
includeMetadataChanges : options ?. includeMetadataChanges ,
96
81
} ,
97
82
( snapshot ) => {
98
- if ( ! resolved ) {
99
- resolved = true ;
100
- return resolve ( snapshot ) ;
101
- } else {
102
- client . setQueryData < DocumentSnapshot < T > > ( key , snapshot ) ;
83
+ // Set the data each time state changes.
84
+ client . setQueryData < DocumentSnapshot < T > > ( key , snapshot ) ;
85
+
86
+ // Resolve the completer with the current data.
87
+ if ( ! completer . current ! . completed ) {
88
+ completer . current ! . complete ( snapshot ) ;
103
89
}
104
90
} ,
105
- reject
91
+ ( error ) => completer . current ! . reject ( error )
106
92
) ;
107
- } ) ;
108
- } ,
109
- } ) ;
110
- }
111
-
112
- export function useFirestoreDocumentData <
113
- T = DocumentData ,
114
- R = WithIdField < T > | undefined
115
- > (
116
- key : QueryKey ,
117
- ref : DocumentReference < T > ,
118
- options ?: UseFirestoreHookOptions & SnapshotOptions ,
119
- useQueryOptions ?: Omit <
120
- UseQueryOptions < WithIdField < T > | undefined , FirestoreError , R > ,
121
- "queryFn"
122
- >
123
- ) : UseQueryResult < R , FirestoreError > ;
124
-
125
- export function useFirestoreDocumentData <
126
- ID extends string ,
127
- T = DocumentData ,
128
- R = WithIdField < T , ID > | undefined
129
- > (
130
- key : QueryKey ,
131
- ref : DocumentReference < T > ,
132
- options ?: UseFirestoreHookOptions & SnapshotOptions & { idField : ID } ,
133
- useQueryOptions ?: Omit <
134
- UseQueryOptions < WithIdField < T , ID > | undefined , FirestoreError , R > ,
135
- "queryFn"
136
- >
137
- ) : UseQueryResult < R | undefined , FirestoreError > ;
93
+ } else {
94
+ // Since there is already an active subscription, resolve the completer
95
+ // with the cached data.
96
+ completer . current ! . complete (
97
+ client . getQueryData ( key ) as DocumentSnapshot < T >
98
+ ) ;
99
+ }
138
100
139
- export function useFirestoreDocumentData <
140
- ID extends string ,
141
- T = DocumentData ,
142
- R = WithIdField < T , ID > | undefined
143
- > (
144
- key : QueryKey ,
145
- ref : DocumentReference < T > ,
146
- options ?: UseFirestoreHookOptions & SnapshotOptions & { idField ?: ID } ,
147
- useQueryOptions ?: Omit <
148
- UseQueryOptions < WithIdField < T , ID > | undefined , FirestoreError , R > ,
149
- "queryFn"
150
- >
151
- ) : UseQueryResult < R , FirestoreError > {
152
- const client = useQueryClient ( ) ;
153
- const unsubscribe = useRef < Unsubscribe > ( ) ;
101
+ return ( ) => {
102
+ counts [ hash ] -- ;
154
103
155
- useEffect ( ( ) => {
156
- return ( ) => unsubscribe . current ?.( ) ;
157
- } , [ ] ) ;
104
+ if ( counts [ hash ] === 0 ) {
105
+ subscriptions [ hash ] ( ) ;
106
+ delete subscriptions [ hash ] ;
107
+ }
108
+ } ;
109
+ }
110
+ } , [ isSubscription , hash , completer ] ) ;
158
111
159
- return useQuery < WithIdField < T , ID > | undefined , FirestoreError , R > ( {
112
+ return useQuery < DocumentSnapshot < T > , FirestoreError , R > ( {
160
113
...useQueryOptions ,
161
114
queryKey : useQueryOptions ?. queryKey ?? key ,
162
- staleTime :
163
- useQueryOptions ?. staleTime ?? options ?. subscribe ? Infinity : undefined ,
164
- async queryFn ( ) : Promise < WithIdField < T , ID > | undefined > {
165
- unsubscribe . current ?.( ) ;
166
-
167
- if ( ! options ?. subscribe ) {
168
- const snapshot = await getSnapshot ( ref , options ?. source ) ;
169
-
170
- let data = snapshot . data ( {
171
- serverTimestamps : options ?. serverTimestamps ,
172
- } ) ;
173
-
174
- if ( data && options ?. idField ) {
175
- data = {
176
- ...data ,
177
- [ options . idField ] : snapshot . id ,
178
- } ;
179
- }
180
-
181
- return data as WithIdField < T , ID > | undefined ;
182
- }
183
-
184
- let resolved = false ;
185
-
186
- return new Promise < WithIdField < T , ID > | undefined > ( ( resolve , reject ) => {
187
- unsubscribe . current = onSnapshot (
188
- ref ,
189
- {
190
- includeMetadataChanges : options ?. includeMetadataChanges ,
191
- } ,
192
- ( snapshot ) => {
193
- let data = snapshot . data ( {
194
- serverTimestamps : options ?. serverTimestamps ,
195
- } ) ;
196
-
197
- if ( data && options ?. idField ) {
198
- data = {
199
- ...data ,
200
- [ options . idField ] : snapshot . id ,
201
- } ;
202
- }
203
-
204
- if ( ! resolved ) {
205
- resolved = true ;
206
- return resolve ( data as WithIdField < T , ID > | undefined ) ;
207
- } else {
208
- client . setQueryData < WithIdField < T , ID > | undefined > (
209
- key ,
210
- data as WithIdField < T , ID > | undefined
211
- ) ;
212
- }
213
- } ,
214
- reject
215
- ) ;
216
- } ) ;
217
- } ,
115
+ queryFn : ( ) => completer . current ! . promise ,
218
116
} ) ;
219
117
}
0 commit comments