Skip to content

Commit 0c70368

Browse files
committed
[firestore] Add manual reload functionality to *Once hooks
1 parent 416de01 commit 0c70368

File tree

6 files changed

+207
-143
lines changed

6 files changed

+207
-143
lines changed

firestore/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ const FirestoreDocument = () => {
208208
### useDocumentOnce
209209

210210
```js
211-
const [snapshot, loading, error] = useDocumentOnce(reference, options);
211+
const [snapshot, loading, error, reload] = useDocumentOnce(reference, options);
212212
```
213213

214214
Retrieve the current value of the `firestore.DocumentReference`.
@@ -225,6 +225,7 @@ Returns:
225225
- `snapshot`: a `firestore.DocumentSnapshot`, or `undefined` if no reference is supplied
226226
- `loading`: a `boolean` to indicate if the data is still being loaded
227227
- `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error
228+
- `reload()`: a function that can be called to trigger a reload of the data
228229

229230
### useDocumentData
230231

@@ -253,7 +254,7 @@ Returns:
253254
### useDocumentDataOnce
254255

255256
```js
256-
const [value, loading, error, snapshot] =
257+
const [value, loading, error, snapshot, reload] =
257258
useDocumentDataOnce < T > (reference, options);
258259
```
259260

@@ -273,6 +274,7 @@ Returns:
273274
- `loading`: a `boolean` to indicate if the data is still being loaded
274275
- `error`: Any `firestore.FirestoreError` returned by Firebase when trying to load the data, or `undefined` if there is no error
275276
- `snapshot`: a `firestore.DocumentSnapshot`, or `undefined` if no query is supplied. This allows access to the underlying snapshot if needed for any reason, e.g. to view the snapshot metadata
277+
- `reload()`: a function that can be called to trigger a reload of the data
276278

277279
## Transforming data
278280

firestore/types.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,32 @@ export type CollectionHook<T = DocumentData> = LoadingHook<
2727
QuerySnapshot<T>,
2828
FirestoreError
2929
>;
30+
export type CollectionOnceHook<T = DocumentData> = [
31+
...CollectionHook<T>,
32+
() => Promise<void>
33+
];
3034
export type CollectionDataHook<T = DocumentData> = [
31-
T[] | undefined,
32-
boolean,
33-
FirestoreError | undefined,
35+
...LoadingHook<T[], FirestoreError>,
3436
QuerySnapshot<T> | undefined
3537
];
38+
export type CollectionDataOnceHook<T = DocumentData> = [
39+
...CollectionDataHook<T>,
40+
() => Promise<void>
41+
];
3642

3743
export type DocumentHook<T = DocumentData> = LoadingHook<
3844
DocumentSnapshot<T>,
3945
FirestoreError
4046
>;
47+
export type DocumentOnceHook<T = DocumentData> = [
48+
...DocumentHook<T>,
49+
() => Promise<void>
50+
];
4151
export type DocumentDataHook<T = DocumentData> = [
42-
T | undefined,
43-
boolean,
44-
FirestoreError | undefined,
52+
...LoadingHook<T, FirestoreError>,
4553
DocumentSnapshot<T> | undefined
4654
];
55+
export type DocumentDataOnceHook<T = DocumentData> = [
56+
...DocumentDataHook<T>,
57+
() => Promise<void>
58+
];

firestore/useCollection.ts

Lines changed: 92 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import {
77
onSnapshot,
88
Query,
99
QuerySnapshot,
10+
SnapshotOptions,
1011
} from 'firebase/firestore';
1112
import { useEffect, useMemo } from 'react';
1213
import { useLoadingValue } from '../util';
1314
import { useIsFirestoreQueryEqual } from './helpers';
1415
import {
1516
CollectionDataHook,
17+
CollectionDataOnceHook,
1618
CollectionHook,
19+
CollectionOnceHook,
1720
DataOptions,
1821
GetOptions,
1922
OnceDataOptions,
@@ -25,110 +28,130 @@ export const useCollection = <T = DocumentData>(
2528
query?: Query<T> | null,
2629
options?: Options
2730
): CollectionHook<T> => {
28-
return useCollectionInternal<T>(true, query, options);
29-
};
31+
const { error, loading, reset, setError, setValue, value } = useLoadingValue<
32+
QuerySnapshot<T>,
33+
FirestoreError
34+
>();
35+
const ref = useIsFirestoreQueryEqual<Query<T>>(query, reset);
3036

31-
export const useCollectionOnce = <T = DocumentData>(
32-
query?: Query<T> | null,
33-
options?: OnceOptions
34-
): CollectionHook<T> => {
35-
return useCollectionInternal<T>(false, query, options);
36-
};
37+
useEffect(() => {
38+
if (!ref.current) {
39+
setValue(undefined);
40+
return;
41+
}
42+
const unsubscribe = options?.snapshotListenOptions
43+
? onSnapshot(
44+
ref.current,
45+
options.snapshotListenOptions,
46+
setValue,
47+
setError
48+
)
49+
: onSnapshot(ref.current, setValue, setError);
3750

38-
export const useCollectionData = <T = DocumentData>(
39-
query?: Query<T> | null,
40-
options?: DataOptions<T>
41-
): CollectionDataHook<T> => {
42-
return useCollectionDataInternal<T>(true, query, options);
43-
};
51+
return () => {
52+
unsubscribe();
53+
};
54+
}, [ref.current, options]);
4455

45-
export const useCollectionDataOnce = <T = DocumentData>(
46-
query?: Query<T> | null,
47-
options?: OnceDataOptions<T>
48-
): CollectionDataHook<T> => {
49-
return useCollectionDataInternal<T>(false, query, options);
56+
const resArray: CollectionHook<T> = [
57+
value as QuerySnapshot<T>,
58+
loading,
59+
error,
60+
];
61+
return useMemo(() => resArray, resArray);
5062
};
5163

52-
const useCollectionInternal = <T = DocumentData>(
53-
listen: boolean,
64+
export const useCollectionOnce = <T = DocumentData>(
5465
query?: Query<T> | null,
55-
options?: Options & OnceOptions
56-
): CollectionHook<T> => {
66+
options?: OnceOptions
67+
): CollectionOnceHook<T> => {
5768
const { error, loading, reset, setError, setValue, value } = useLoadingValue<
5869
QuerySnapshot<T>,
5970
FirestoreError
6071
>();
72+
let effectActive = true;
6173
const ref = useIsFirestoreQueryEqual<Query<T>>(query, reset);
6274

63-
useEffect(() => {
64-
if (!ref.current) {
75+
const loadData = async (
76+
query?: Query<T> | null,
77+
options?: Options & OnceOptions
78+
) => {
79+
if (!query) {
6580
setValue(undefined);
6681
return;
6782
}
68-
if (listen) {
69-
const unsubscribe = options?.snapshotListenOptions
70-
? onSnapshot(
71-
ref.current,
72-
options.snapshotListenOptions,
73-
setValue,
74-
setError
75-
)
76-
: onSnapshot(ref.current, setValue, setError);
83+
const get = getDocsFnFromGetOptions(options?.getOptions);
7784

78-
return () => {
79-
unsubscribe();
80-
};
81-
} else {
82-
let effectActive = true;
83-
84-
const get = getDocsFnFromGetOptions(options?.getOptions);
85+
try {
86+
const result = await get(query);
87+
if (effectActive) {
88+
setValue(result);
89+
}
90+
} catch (error) {
91+
if (effectActive) {
92+
setError(error as FirestoreError);
93+
}
94+
}
95+
};
8596

86-
get(ref.current)
87-
.then((result) => {
88-
if (effectActive) {
89-
setValue(result);
90-
}
91-
})
92-
.catch((error) => {
93-
if (effectActive) {
94-
setError(error);
95-
}
96-
});
97+
useEffect(() => {
98+
loadData(ref.current, options);
9799

98-
return () => {
99-
effectActive = false;
100-
};
101-
}
102-
}, [ref.current]);
100+
return () => {
101+
effectActive = false;
102+
};
103+
}, [ref.current, options]);
103104

104-
const resArray: CollectionHook<T> = [
105+
const resArray: CollectionOnceHook<T> = [
105106
value as QuerySnapshot<T>,
106107
loading,
107108
error,
109+
() => loadData(ref.current, options),
108110
];
109111
return useMemo(() => resArray, resArray);
110112
};
111113

112-
const useCollectionDataInternal = <T = DocumentData>(
113-
listen: boolean,
114+
export const useCollectionData = <T = DocumentData>(
114115
query?: Query<T> | null,
115-
options?: DataOptions<T> & OnceDataOptions<T>
116+
options?: DataOptions<T>
116117
): CollectionDataHook<T> => {
117118
const snapshotOptions = options?.snapshotOptions;
118-
const [snapshots, loading, error] = useCollectionInternal<T>(
119-
listen,
119+
const [snapshots, loading, error] = useCollection<T>(query, options);
120+
const values = getValuesFromSnapshots<T>(snapshots, snapshotOptions);
121+
const resArray: CollectionDataHook<T> = [values, loading, error, snapshots];
122+
return useMemo(() => resArray, resArray);
123+
};
124+
125+
export const useCollectionDataOnce = <T = DocumentData>(
126+
query?: Query<T> | null,
127+
options?: OnceDataOptions<T>
128+
): CollectionDataOnceHook<T> => {
129+
const snapshotOptions = options?.snapshotOptions;
130+
const [snapshots, loading, error, loadData] = useCollectionOnce<T>(
120131
query,
121132
options
122133
);
123-
const values = useMemo(
124-
() => snapshots?.docs.map((doc) => doc.data(snapshotOptions)) as T[],
125-
[snapshots, snapshotOptions]
126-
);
127-
128-
const resArray: CollectionDataHook<T> = [values, loading, error, snapshots];
134+
const values = getValuesFromSnapshots<T>(snapshots, snapshotOptions);
135+
const resArray: CollectionDataOnceHook<T> = [
136+
values,
137+
loading,
138+
error,
139+
snapshots,
140+
loadData,
141+
];
129142
return useMemo(() => resArray, resArray);
130143
};
131144

145+
const getValuesFromSnapshots = <T>(
146+
snapshots?: QuerySnapshot<T>,
147+
options?: SnapshotOptions
148+
) => {
149+
return useMemo(() => snapshots?.docs.map((doc) => doc.data(options)) as T[], [
150+
snapshots,
151+
options,
152+
]);
153+
};
154+
132155
const getDocsFnFromGetOptions = (
133156
{ source }: GetOptions = { source: 'default' }
134157
) => {

0 commit comments

Comments
 (0)