Skip to content

Commit 99ce5e0

Browse files
committed
Add transform option for realtime database hooks. Fixes #91
1 parent e7cd1ef commit 99ce5e0

File tree

4 files changed

+72
-9
lines changed

4 files changed

+72
-9
lines changed

database/README.md

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ import { useList } from 'react-firebase-hooks/database';
1414

1515
List of Realtime Database hooks:
1616

17-
- [useList](#uselistref)
17+
- [useList](#uselist)
1818
- [useListKeys](#uselistkeys)
1919
- [useListVals](#uselistvals)
2020
- [useObject](#useobject)
2121
- [useObjectVal](#useobjectval)
2222

23+
Additional functionality:
24+
25+
- [Transforming data](#transforming-data)
26+
2327
### useList
2428

2529
```js
@@ -100,6 +104,7 @@ The `useListVals` hook takes the following parameters:
100104
- `options`: (optional) `Object` with the following parameters:
101105
- `keyField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.id` property in the returned values.
102106
- `refField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.ref` property.
107+
- `transform`: (optional) a function that receives the raw `firebase.database.DataSnapshot.val()` for each item in the list to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below.
103108

104109
Returns:
105110

@@ -160,9 +165,63 @@ The `useObjectVal` hook takes the following parameters:
160165
- `options`: (optional) `Object` with the following parameters:
161166
- `keyField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.key` property in the returned value.
162167
- `refField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.ref` property.
168+
- `transform`: (optional) a function that receives the raw `firebase.database.DataSnapshot.val()` for each item in the list to allow manual transformation of the data where required by the application. See [`Transforming data`](#transforming-data) below.
163169

164170
Returns:
165171

166172
- `value`: a `T`, or `undefined` if no reference is supplied
167173
- `loading`: a `boolean` to indicate if the data is still being loaded
168174
- `error`: Any `firebase.FirebaseError` returned by Firebase when trying to load the data, or `undefined` if there is no error
175+
176+
## Transforming data
177+
178+
Firebase 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.
179+
180+
Both `useListVals` and `useObjectVal` support an optional `transform` function which allows the transformation of the underlying Firebase data into whatever format the application requires.
181+
182+
```js
183+
transform?: (val: any) => T;
184+
```
185+
186+
The `transform` function is passed a single row of a data, so will be called once when used with `useObjectVal` and multiple times, when used with `useListVals`.
187+
188+
The `transform` function will not receive the `id` or `ref` values referenced in the properties named in the `keyField` or `refField` options, nor it is expected to produce them. Either or both, if specified, will be merged afterwards.
189+
190+
#### Full Example
191+
192+
```js
193+
type SaleType = {
194+
idSale: string,
195+
date: Date, // <== it is declared as type Date which Firebase does not support.
196+
// ...Other fields
197+
};
198+
const options = {
199+
keyField: 'idSale',
200+
transform: (val) => ({
201+
...val,
202+
date: new Date(val.date),
203+
}),
204+
};
205+
export const useSale: (
206+
idSale: string
207+
) => [SaleType | undefined, boolean, any] = (idSale) =>
208+
useObjectVal < SaleType > (database.ref(`sales/${idSale}`), options);
209+
export const useSales: () => [SaleType[] | undefined, boolean, any] = () =>
210+
useListVals < SaleType > (database.ref('sales'), options);
211+
```
212+
213+
The `transform` function might be used for various purposes:
214+
215+
```js
216+
transform: ({ firstName, lastName, someBool, ...val }) => ({
217+
// Merge in default values, declared elsewhere:
218+
...defaultValues,
219+
// Override them with the actual values
220+
...val,
221+
// Create new fields from existing values
222+
fullName: `${firstName} ${lastName}`,
223+
// Ensure a field is a proper boolean instead of truish or falsy:
224+
someBool: !!someBool,
225+
// Same goes for any other poorly represented data type
226+
});
227+
```

database/helpers/index.ts

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

3-
export type ValOptions = {
3+
export type ValOptions<T> = {
44
keyField?: string;
55
refField?: string;
6+
transform?: (val: any) => T;
67
};
78

89
const isObject = (val: any) =>
910
val != null && typeof val === 'object' && Array.isArray(val) === false;
1011

11-
export const snapshotToData = (
12+
export const snapshotToData = <T>(
1213
snapshot: firebase.database.DataSnapshot,
1314
keyField?: string,
14-
refField?: string
15+
refField?: string,
16+
transform?: (val: any) => T
1517
) => {
1618
if (!snapshot.exists) {
1719
return undefined;
@@ -20,7 +22,7 @@ export const snapshotToData = (
2022
const val = snapshot.val();
2123
if (isObject(val)) {
2224
return {
23-
...val,
25+
...(transform ? transform(val) : val),
2426
...(keyField ? { [keyField]: snapshot.key } : null),
2527
...(refField ? { [refField]: snapshot.ref } : null),
2628
};

database/useList.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,17 @@ export const useListVals = <
139139
RefField extends string = ''
140140
>(
141141
query?: firebase.database.Query | null,
142-
options?: ValOptions
142+
options?: ValOptions<T>
143143
): ListValsHook<T, KeyField, RefField> => {
144144
const keyField = options ? options.keyField : undefined;
145145
const refField = options ? options.refField : undefined;
146+
const transform = options ? options.transform : undefined;
146147
const [snapshots, loading, error] = useList(query);
147148
const values = useMemo(
148149
() =>
149150
(snapshots
150151
? snapshots.map((snapshot) =>
151-
snapshotToData(snapshot, keyField, refField)
152+
snapshotToData(snapshot, keyField, refField, transform)
152153
)
153154
: undefined) as Val<T, KeyField, RefField>[],
154155
[snapshots, keyField, refField]

database/useObject.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@ export const useObjectVal = <
3737
RefField extends string = ''
3838
>(
3939
query?: firebase.database.Query | null,
40-
options?: ValOptions
40+
options?: ValOptions<T>
4141
): ObjectValHook<T, KeyField, RefField> => {
4242
const keyField = options ? options.keyField : undefined;
4343
const refField = options ? options.refField : undefined;
44+
const transform = options ? options.transform : undefined;
4445
const [snapshot, loading, error] = useObject(query);
4546
const value = useMemo(
4647
() =>
4748
(snapshot
48-
? snapshotToData(snapshot, keyField, refField)
49+
? snapshotToData(snapshot, keyField, refField, transform)
4950
: undefined) as Val<T, KeyField, RefField>,
5051
[snapshot, keyField, refField]
5152
);

0 commit comments

Comments
 (0)