Skip to content

Commit 4b90236

Browse files
authored
Add *Data methods for RTDB (#184)
* extract checkOptions and friends * add useDatabaseListData and useDatabaseObjectData * update sample * update types * add to docs
1 parent fb1c516 commit 4b90236

File tree

5 files changed

+158
-28
lines changed

5 files changed

+158
-28
lines changed

docs/reference.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@
2222
- Database
2323
- Cloud Firestore
2424
- [`useFirestoreDoc`](#useFirestoreDoc)
25+
- [`useFirestoreDocData`](#useFirestoreDocData)
2526
- [`useFirestoreCollection`](#useFirestoreCollection)
27+
- [`useFirestoreCollectionData`](#useFirestoreCollectionData)
2628
- Realtime Database
2729
- [`useDatabaseObject`](#useDatabaseObject)
30+
- [`useDatabaseObjectData`](#useDatabaseObjectData)
2831
- [`useDatabaseList`](#useDatabaseList)
32+
- [`useDatabaseListData`](#useDatabaseListData)
2933
- Cloud Storage
3034
- [`useStorageTask`](#useStorageTask)
3135
- [`useStorageDownloadURL`](#useStorageDownloadURL)
@@ -187,6 +191,23 @@ _Throws a Promise by default_
187191

188192
[`DocumentSnapshot`](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentSnapshot)
189193

194+
### `useFirestoreDocData`
195+
196+
Listen to a Firestore Document.
197+
198+
_Throws a Promise by default_
199+
200+
#### Parameters
201+
202+
| Parameter | Type | Description |
203+
| ----------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
204+
| ref | [`DocumentReference`](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference) | A reference to the document you want to listen to |
205+
| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. |
206+
207+
#### Returns
208+
209+
`T`
210+
190211
### `useFirestoreCollection`
191212

192213
Listen to a Firestore Collection.
@@ -204,6 +225,23 @@ _Throws a Promise by default_
204225

205226
[`QuerySnapshot`](https://firebase.google.com/docs/reference/js/firebase.firestore.QuerySnapshot)
206227

228+
### `useFirestoreCollectionData`
229+
230+
Listen to a Firestore Collection.
231+
232+
_Throws a Promise by default_
233+
234+
#### Parameters
235+
236+
| Parameter | Type | Description |
237+
| ----------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
238+
| ref | [`Query`](https://firebase.google.com/docs/reference/js/firebase.firestore.Query) | A query for the collection you want to listen to |
239+
| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. |
240+
241+
#### Returns
242+
243+
`T[]`
244+
207245
### `useDatabaseObject`
208246

209247
Listen to a Realtime Database Object.
@@ -221,6 +259,23 @@ _Throws a Promise by default_
221259

222260
[`QueryChange`](https://github.com/firebase/firebase-js-sdk/blob/6b53e0058483c9002d2fe56119f86fc9fb96b56c/packages/rxfire/database/interfaces.ts#L28)
223261

262+
### `useDatabaseObjectData`
263+
264+
Listen to a Realtime Database Object.
265+
266+
_Throws a Promise by default_
267+
268+
#### Parameters
269+
270+
| Parameter | Type | Description |
271+
| ----------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
272+
| ref | [`Reference`](https://firebase.google.com/docs/reference/js/firebase.database.Reference) | A reference to the object you want to listen to |
273+
| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. |
274+
275+
#### Returns
276+
277+
`T`
278+
224279
### `useDatabaseList`
225280

226281
Listen to a Realtime Database list.
@@ -238,6 +293,23 @@ _Throws a Promise by default_
238293

239294
[`QueryChange[]`](https://github.com/firebase/firebase-js-sdk/blob/6b53e0058483c9002d2fe56119f86fc9fb96b56c/packages/rxfire/database/interfaces.ts#L28)
240295

296+
### `useDatabaseListData`
297+
298+
Listen to a Realtime Database list.
299+
300+
_Throws a Promise by default_
301+
302+
#### Parameters
303+
304+
| Parameter | Type | Description |
305+
| ----------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
306+
| ref | [`Reference`](https://firebase.google.com/docs/reference/js/firebase.database.Reference) | A reference to the list you want to listen to |
307+
| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. |
308+
309+
#### Returns
310+
311+
`T[]`
312+
241313
### `useStorageTask`
242314

243315
Listen to a Storage UploadTask

reactfire/database/index.tsx

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import { database } from 'firebase/app';
2-
import { list, object, QueryChange } from 'rxfire/database';
3-
import { ReactFireOptions, useObservable } from '..';
2+
import { list, object, QueryChange, listVal } from 'rxfire/database';
3+
import {
4+
ReactFireOptions,
5+
useObservable,
6+
checkIdField,
7+
checkStartWithValue
8+
} from '..';
9+
10+
import { Observable } from 'rxjs';
11+
import { map } from 'rxjs/operators';
412

513
/**
614
* Subscribe to a Realtime Database object
@@ -14,11 +22,45 @@ export function useDatabaseObject<T = unknown>(
1422
): QueryChange | T {
1523
return useObservable(
1624
object(ref),
17-
`RTDB: ${ref.toString()}`,
25+
`RTDB Doc: ${ref.toString()}`,
1826
options ? options.startWithValue : undefined
1927
);
2028
}
2129

30+
// ============================================================================
31+
// TODO: switch to rxfire's objectVal once this PR is merged:
32+
// https://github.com/firebase/firebase-js-sdk/pull/2352
33+
34+
function objectVal<T>(query: database.Query, keyField?: string): Observable<T> {
35+
return object(query).pipe(map(change => changeToData(change, keyField) as T));
36+
}
37+
38+
function changeToData(change: QueryChange, keyField?: string): {} {
39+
const val = change.snapshot.val();
40+
41+
// don't worry about setting IDs if the value is a primitive type
42+
if (typeof val !== 'object') {
43+
return val;
44+
}
45+
46+
return {
47+
...change.snapshot.val(),
48+
...(keyField ? { [keyField]: change.snapshot.key } : null)
49+
};
50+
}
51+
// ============================================================================
52+
53+
export function useDatabaseObjectData<T>(
54+
ref: database.Reference,
55+
options?: ReactFireOptions<T>
56+
): T {
57+
return useObservable(
58+
objectVal(ref, checkIdField(options)),
59+
`RTDB DocData: ${ref.toString()}`,
60+
checkStartWithValue(options)
61+
);
62+
}
63+
2264
// Realtime Database has an undocumented method
2365
// that helps us build a unique ID for the query
2466
// https://github.com/firebase/firebase-js-sdk/blob/aca99669dd8ed096f189578c47a56a8644ac62e6/packages/database/src/api/Query.ts#L601
@@ -36,11 +78,22 @@ export function useDatabaseList<T = { [key: string]: unknown }>(
3678
ref: database.Reference | database.Query,
3779
options?: ReactFireOptions<T[]>
3880
): QueryChange[] | T[] {
39-
const hash = `RTDB: ${ref.toString()}|${(ref as _QueryWithId).queryIdentifier()}`;
81+
const hash = `RTDB List: ${ref.toString()}|${(ref as _QueryWithId).queryIdentifier()}`;
4082

4183
return useObservable(
4284
list(ref),
4385
hash,
4486
options ? options.startWithValue : undefined
4587
);
4688
}
89+
90+
export function useDatabaseListData<T = { [key: string]: unknown }>(
91+
ref: database.Reference | database.Query,
92+
options?: ReactFireOptions<T[]>
93+
): T[] {
94+
return useObservable(
95+
listVal(ref, checkIdField(options)),
96+
`RTDB ListData: ${ref.toString()}`,
97+
checkStartWithValue(options)
98+
);
99+
}

reactfire/firestore/index.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import {
55
docData,
66
fromCollectionRef
77
} from 'rxfire/firestore';
8-
import { preloadFirestore, ReactFireOptions, useObservable } from '..';
8+
import {
9+
preloadFirestore,
10+
ReactFireOptions,
11+
useObservable,
12+
checkIdField,
13+
checkStartWithValue
14+
} from '..';
915
import { preloadObservable } from '../useObservable';
1016

1117
// starts a request for a firestore doc.
@@ -112,15 +118,3 @@ export function useFirestoreCollectionData<T = { [key: string]: unknown }>(
112118
checkStartWithValue(options)
113119
);
114120
}
115-
116-
function checkOptions(options: ReactFireOptions, field: string) {
117-
return options ? options[field] : undefined;
118-
}
119-
120-
function checkStartWithValue(options: ReactFireOptions) {
121-
return checkOptions(options, 'startWithValue');
122-
}
123-
124-
function checkIdField(options: ReactFireOptions) {
125-
return checkOptions(options, 'idField');
126-
}

reactfire/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ export interface ReactFireOptions<T = unknown> {
33
idField?: string;
44
}
55

6+
export function checkOptions(options: ReactFireOptions, field: string) {
7+
return options ? options[field] : undefined;
8+
}
9+
10+
export function checkStartWithValue(options: ReactFireOptions) {
11+
return checkOptions(options, 'startWithValue');
12+
}
13+
14+
export function checkIdField(options: ReactFireOptions) {
15+
return checkOptions(options, 'idField');
16+
}
17+
618
export * from './auth';
719
export * from './database';
820
export * from './firebaseApp';

sample/src/RealtimeDatabase.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import React, { useState } from 'react';
22
import {
33
AuthCheck,
44
SuspenseWithPerf,
5-
useDatabaseList,
6-
useDatabaseObject,
7-
useDatabase
5+
useDatabaseObjectData,
6+
useDatabase,
7+
useDatabaseListData
88
} from 'reactfire';
99

1010
const Counter = props => {
@@ -16,13 +16,12 @@ const Counter = props => {
1616
});
1717
};
1818

19-
const { snapshot } = useDatabaseObject(ref);
20-
const counterValue = snapshot.val();
19+
const count = useDatabaseObjectData(ref);
2120

2221
return (
2322
<>
2423
<button onClick={() => increment(-1)}>-</button>
25-
<span> {counterValue} </span>
24+
<span> {count} </span>
2625
<button onClick={() => increment(1)}>+</button>
2726
</>
2827
);
@@ -58,7 +57,8 @@ const AnimalEntry = ({ saveAnimal }) => {
5857
const List = props => {
5958
const database = useDatabase();
6059
const ref = database().ref('animals');
61-
const changes = useDatabaseList(ref);
60+
const animals = useDatabaseListData(ref, { idField: 'id' });
61+
6262
const addNewAnimal = commonName => {
6363
const newAnimalRef = ref.push();
6464
return newAnimalRef.set({
@@ -72,10 +72,9 @@ const List = props => {
7272
<>
7373
<AnimalEntry saveAnimal={addNewAnimal} />
7474
<ul>
75-
{changes.map(({ snapshot }) => (
76-
<li key={snapshot.key}>
77-
{snapshot.val().commonName}{' '}
78-
<button onClick={() => removeAnimal(snapshot.key)}>X</button>
75+
{animals.map(({ commonName, id }) => (
76+
<li key={id}>
77+
{commonName} <button onClick={() => removeAnimal(id)}>X</button>
7978
</li>
8079
))}
8180
</ul>

0 commit comments

Comments
 (0)