Skip to content

Commit e9b757b

Browse files
committed
Add transform option to Firestore hooks. Closes #62
1 parent ae84719 commit e9b757b

File tree

5 files changed

+55
-15
lines changed

5 files changed

+55
-15
lines changed

firestore/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ List of Cloud Firestore hooks:
2929
- [useDocumentData](#usedocumentdata)
3030
- [useDocumentDataOnce](#usedocumentdataonce)
3131

32+
Additional functionality:
33+
34+
- [Transforming data](#transforming-data)
35+
3236
### useCollection
3337

3438
```js
@@ -121,6 +125,7 @@ The `useCollectionData` hook takes the following parameters:
121125
- `refField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.ref` property.
122126
- `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the collection is loaded
123127
- `snapshotOptions`: (optional) `firebase.firestore.SnapshotOptions` to customise how data is retrieved from snapshots
128+
- `transform`: (optional) a function that receives the raw `firebase.firestore.DocumentData` for each item in the collection to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below.
124129

125130
Returns:
126131

@@ -144,6 +149,7 @@ The `useCollectionDataOnce` hook takes the following parameters:
144149
- `idField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.id` property.
145150
- `refField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.ref` property.
146151
- `snapshotOptions`: (optional) `firebase.firestore.SnapshotOptions` to customise how data is retrieved from snapshots
152+
- `transform`: (optional) a function that receives the raw `firebase.firestore.DocumentData` for each item in the collection to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below.
147153

148154
Returns:
149155

@@ -232,6 +238,7 @@ The `useDocumentData` hook takes the following parameters:
232238
- `refField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.ref` property.
233239
- `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the collection is loaded
234240
- `snapshotOptions`: (optional) `firebase.firestore.SnapshotOptions` to customise how data is retrieved from snapshots
241+
- `transform`: (optional) a function that receives the raw `firebase.firestore.DocumentData` to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below.
235242

236243
Returns:
237244

@@ -255,9 +262,24 @@ The `useDocumentDataOnce` hook takes the following parameters:
255262
- `idField`: (optional) name of the field that should be populated with the `firebase.firestore.DocumentSnapshot.id` property.
256263
- `refField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.ref` property.
257264
- `snapshotOptions`: (optional) `firebase.firestore.SnapshotOptions` to customise how data is retrieved from snapshots
265+
- `transform`: (optional) a function that receives the raw `firebase.firestore.DocumentData` to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below.
258266

259267
Returns:
260268

261269
- `value`: `T`, or `undefined` if no query is supplied
262270
- `loading`: a `boolean` to indicate if the data is still being loaded
263271
- `error`: Any `Error` returned by Firebase when trying to load the data, or `undefined` if there is no error
272+
273+
## Transforming data
274+
275+
Firestore allows a restricted number of data types in its store. The application, on the other hand, might require converting some of these types into whatever it really needs, `Date` types being a common case.
276+
277+
Both `useCollectionData` and `useDocumentData` support an optional `transform` function which allows the transformation of the underlying Firestore data into whatever format the application requires.
278+
279+
```js
280+
transform?: (val: any) => T;
281+
```
282+
283+
The `transform` function is passed a single row of a data, so will be called once when used with `useDocumentData` and multiple times when used with `useCollectionData`.
284+
285+
The `transform` function will not receive the `id` or `ref` values referenced in the properties named in the `idField` or `refField` options, nor it is expected to produce them. Either or both, if specified, will be merged afterwards.

firestore/helpers/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import firebase from 'firebase/app';
22

3-
export const snapshotToData = (
3+
export const snapshotToData = <T>(
44
snapshot: firebase.firestore.DocumentSnapshot,
55
snapshotOptions?: firebase.firestore.SnapshotOptions,
66
idField?: string,
7-
refField?: string
7+
refField?: string,
8+
transform?: (val: any) => T
89
) => {
910
if (!snapshot.exists) {
1011
return undefined;
1112
}
1213

1314
return {
14-
...snapshot.data(snapshotOptions),
15+
...(transform
16+
? transform(snapshot.data(snapshotOptions))
17+
: snapshot.data(snapshotOptions)),
1518
...(idField ? { [idField]: snapshot.id } : null),
1619
...(refField ? { [refField]: snapshot.ref } : null),
1720
};

firestore/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import firebase from 'firebase/app';
22
import { LoadingHook } from '../util';
33

4-
type IDOptions = {
4+
type IDOptions<T> = {
55
idField?: string;
66
refField?: string;
77
snapshotOptions?: firebase.firestore.SnapshotOptions;
8+
transform?: (val: any) => T;
89
};
910
export type Options = {
1011
snapshotListenOptions?: firebase.firestore.SnapshotListenOptions;
1112
};
12-
export type DataOptions = Options & IDOptions;
13+
export type DataOptions<T> = Options & IDOptions<T>;
1314
export type OnceOptions = {
1415
getOptions?: firebase.firestore.GetOptions;
1516
};
16-
export type OnceDataOptions = OnceOptions & IDOptions;
17+
export type OnceDataOptions<T> = OnceOptions & IDOptions<T>;
1718
export type Data<
1819
T = firebase.firestore.DocumentData,
1920
IDField extends string = '',

firestore/useCollection.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const useCollectionData = <
3232
RefField extends string = ''
3333
>(
3434
query?: firebase.firestore.Query<T> | null,
35-
options?: DataOptions
35+
options?: DataOptions<T>
3636
): CollectionDataHook<T, IDField, RefField> => {
3737
return useCollectionDataInternal(true, query, options);
3838
};
@@ -43,7 +43,7 @@ export const useCollectionDataOnce = <
4343
RefField extends string = ''
4444
>(
4545
query?: firebase.firestore.Query<T> | null,
46-
options?: OnceDataOptions
46+
options?: OnceDataOptions<T>
4747
): CollectionDataHook<T, IDField, RefField> => {
4848
return useCollectionDataInternal(false, query, options);
4949
};
@@ -100,11 +100,12 @@ const useCollectionDataInternal = <
100100
>(
101101
listen: boolean,
102102
query?: firebase.firestore.Query<T> | null,
103-
options?: DataOptions & OnceDataOptions
103+
options?: DataOptions<T> & OnceDataOptions<T>
104104
): CollectionDataHook<T, IDField, RefField> => {
105105
const idField = options ? options.idField : undefined;
106106
const refField = options ? options.refField : undefined;
107107
const snapshotOptions = options ? options.snapshotOptions : undefined;
108+
const transform = options ? options.transform : undefined;
108109
const [snapshots, loading, error] = useCollectionInternal<T>(
109110
listen,
110111
query,
@@ -114,10 +115,16 @@ const useCollectionDataInternal = <
114115
() =>
115116
(snapshots
116117
? snapshots.docs.map((doc) =>
117-
snapshotToData(doc, snapshotOptions, idField, refField)
118+
snapshotToData<T>(
119+
doc,
120+
snapshotOptions,
121+
idField,
122+
refField,
123+
transform
124+
)
118125
)
119126
: undefined) as Data<T, IDField, RefField>[],
120-
[snapshots, idField, refField]
127+
[snapshots, idField, refField, transform]
121128
);
122129

123130
const resArray: CollectionDataHook<T, IDField, RefField> = [

firestore/useDocument.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const useDocumentData = <
3232
RefField extends string = ''
3333
>(
3434
docRef?: firebase.firestore.DocumentReference<T> | null,
35-
options?: DataOptions
35+
options?: DataOptions<T>
3636
): DocumentDataHook<T, IDField, RefField> => {
3737
return useDocumentDataInternal(true, docRef, options);
3838
};
@@ -43,7 +43,7 @@ export const useDocumentDataOnce = <
4343
RefField extends string = ''
4444
>(
4545
docRef?: firebase.firestore.DocumentReference<T> | null,
46-
options?: OnceDataOptions
46+
options?: OnceDataOptions<T>
4747
): DocumentDataHook<T, IDField, RefField> => {
4848
return useDocumentDataInternal(false, docRef, options);
4949
};
@@ -100,11 +100,12 @@ const useDocumentDataInternal = <
100100
>(
101101
listen: boolean,
102102
docRef?: firebase.firestore.DocumentReference<T> | null,
103-
options?: DataOptions
103+
options?: DataOptions<T>
104104
): DocumentDataHook<T, IDField, RefField> => {
105105
const idField = options ? options.idField : undefined;
106106
const refField = options ? options.refField : undefined;
107107
const snapshotOptions = options ? options.snapshotOptions : undefined;
108+
const transform = options ? options.transform : undefined;
108109
const [snapshot, loading, error] = useDocumentInternal<T>(
109110
listen,
110111
docRef,
@@ -113,7 +114,13 @@ const useDocumentDataInternal = <
113114
const value = useMemo(
114115
() =>
115116
(snapshot
116-
? snapshotToData(snapshot, snapshotOptions, idField, refField)
117+
? snapshotToData(
118+
snapshot,
119+
snapshotOptions,
120+
idField,
121+
refField,
122+
transform
123+
)
117124
: undefined) as Data<T, IDField, RefField>,
118125
[snapshot, idField, refField]
119126
);

0 commit comments

Comments
 (0)