From cf3af00a35d8ea111bf865aeafafba7ff3ef3415 Mon Sep 17 00:00:00 2001 From: jhuleatt Date: Tue, 12 Nov 2019 16:28:20 -0800 Subject: [PATCH 1/5] extract checkOptions and friends --- reactfire/firestore/index.tsx | 20 +++++++------------- reactfire/index.ts | 12 ++++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/reactfire/firestore/index.tsx b/reactfire/firestore/index.tsx index 5eee24b6..da5ee8e7 100644 --- a/reactfire/firestore/index.tsx +++ b/reactfire/firestore/index.tsx @@ -5,7 +5,13 @@ import { docData, fromCollectionRef } from 'rxfire/firestore'; -import { preloadFirestore, ReactFireOptions, useObservable } from '..'; +import { + preloadFirestore, + ReactFireOptions, + useObservable, + checkIdField, + checkStartWithValue +} from '..'; import { preloadObservable } from '../useObservable'; // starts a request for a firestore doc. @@ -112,15 +118,3 @@ export function useFirestoreCollectionData( checkStartWithValue(options) ); } - -function checkOptions(options: ReactFireOptions, field: string) { - return options ? options[field] : undefined; -} - -function checkStartWithValue(options: ReactFireOptions) { - return checkOptions(options, 'startWithValue'); -} - -function checkIdField(options: ReactFireOptions) { - return checkOptions(options, 'idField'); -} diff --git a/reactfire/index.ts b/reactfire/index.ts index b5e0f217..803bf322 100644 --- a/reactfire/index.ts +++ b/reactfire/index.ts @@ -3,6 +3,18 @@ export interface ReactFireOptions { idField?: string; } +export function checkOptions(options: ReactFireOptions, field: string) { + return options ? options[field] : undefined; +} + +export function checkStartWithValue(options: ReactFireOptions) { + return checkOptions(options, 'startWithValue'); +} + +export function checkIdField(options: ReactFireOptions) { + return checkOptions(options, 'idField'); +} + export * from './auth'; export * from './database'; export * from './firebaseApp'; From aa9e9aaa39fbc3d83454dc27dee3893a3439c275 Mon Sep 17 00:00:00 2001 From: jhuleatt Date: Tue, 12 Nov 2019 16:28:53 -0800 Subject: [PATCH 2/5] add useDatabaseListData and useDatabaseObjectData --- reactfire/database/index.tsx | 61 +++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/reactfire/database/index.tsx b/reactfire/database/index.tsx index 297d5197..8e48cd29 100644 --- a/reactfire/database/index.tsx +++ b/reactfire/database/index.tsx @@ -1,6 +1,14 @@ import { database } from 'firebase/app'; -import { list, object, QueryChange } from 'rxfire/database'; -import { ReactFireOptions, useObservable } from '..'; +import { list, object, QueryChange, listVal } from 'rxfire/database'; +import { + ReactFireOptions, + useObservable, + checkIdField, + checkStartWithValue +} from '..'; + +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; /** * Subscribe to a Realtime Database object @@ -14,11 +22,45 @@ export function useDatabaseObject( ): QueryChange | T { return useObservable( object(ref), - `RTDB: ${ref.toString()}`, + `RTDB Doc: ${ref.toString()}`, options ? options.startWithValue : undefined ); } +// ============================================================================ +// TODO: switch to rxfire's objectVal once this PR is merged: +// https://github.com/firebase/firebase-js-sdk/pull/2352 + +function objectVal(query: database.Query, keyField?: string): Observable { + return object(query).pipe(map(change => changeToData(change, keyField) as T)); +} + +function changeToData(change: QueryChange, keyField?: string): {} { + const val = change.snapshot.val(); + + // don't worry about setting IDs if the value is a primitive type + if (typeof val !== 'object') { + return val; + } + + return { + ...change.snapshot.val(), + ...(keyField ? { [keyField]: change.snapshot.key } : null) + }; +} +// ============================================================================ + +export function useDatabaseObjectData( + ref: database.Reference, + options?: ReactFireOptions +): T { + return useObservable( + objectVal(ref, checkIdField(options)), + `RTDB DocData: ${ref.toString()}`, + checkStartWithValue(options) + ); +} + // Realtime Database has an undocumented method // that helps us build a unique ID for the query // https://github.com/firebase/firebase-js-sdk/blob/aca99669dd8ed096f189578c47a56a8644ac62e6/packages/database/src/api/Query.ts#L601 @@ -36,7 +78,7 @@ export function useDatabaseList( ref: database.Reference | database.Query, options?: ReactFireOptions ): QueryChange[] | T[] { - const hash = `RTDB: ${ref.toString()}|${(ref as _QueryWithId).queryIdentifier()}`; + const hash = `RTDB List: ${ref.toString()}|${(ref as _QueryWithId).queryIdentifier()}`; return useObservable( list(ref), @@ -44,3 +86,14 @@ export function useDatabaseList( options ? options.startWithValue : undefined ); } + +export function useDatabaseListData( + ref: database.Reference | database.Query, + options?: ReactFireOptions +): T { + return useObservable( + listVal(ref, checkIdField(options)), + `RTDB ListData: ${ref.toString()}`, + checkStartWithValue(options) + ); +} From 0cc54ac30bfd63b1da55c5f81f1cb9eb441e4714 Mon Sep 17 00:00:00 2001 From: jhuleatt Date: Tue, 12 Nov 2019 16:29:03 -0800 Subject: [PATCH 3/5] update sample --- sample/src/RealtimeDatabase.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sample/src/RealtimeDatabase.js b/sample/src/RealtimeDatabase.js index 395696f2..3c64dcdf 100644 --- a/sample/src/RealtimeDatabase.js +++ b/sample/src/RealtimeDatabase.js @@ -2,9 +2,9 @@ import React, { useState } from 'react'; import { AuthCheck, SuspenseWithPerf, - useDatabaseList, - useDatabaseObject, - useDatabase + useDatabaseObjectData, + useDatabase, + useDatabaseListData } from 'reactfire'; const Counter = props => { @@ -16,13 +16,12 @@ const Counter = props => { }); }; - const { snapshot } = useDatabaseObject(ref); - const counterValue = snapshot.val(); + const count = useDatabaseObjectData(ref); return ( <> - {counterValue} + {count} ); @@ -58,7 +57,8 @@ const AnimalEntry = ({ saveAnimal }) => { const List = props => { const database = useDatabase(); const ref = database().ref('animals'); - const changes = useDatabaseList(ref); + const animals = useDatabaseListData(ref, { idField: 'id' }); + const addNewAnimal = commonName => { const newAnimalRef = ref.push(); return newAnimalRef.set({ @@ -72,10 +72,9 @@ const List = props => { <>
    - {changes.map(({ snapshot }) => ( -
  • - {snapshot.val().commonName}{' '} - + {animals.map(({ commonName, id }) => ( +
  • + {commonName}
  • ))}
From d7eef55fcf9bdffb782eff1eac17451665a1f252 Mon Sep 17 00:00:00 2001 From: jhuleatt Date: Tue, 12 Nov 2019 16:40:35 -0800 Subject: [PATCH 4/5] update types --- reactfire/database/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reactfire/database/index.tsx b/reactfire/database/index.tsx index 8e48cd29..40fcc154 100644 --- a/reactfire/database/index.tsx +++ b/reactfire/database/index.tsx @@ -87,10 +87,10 @@ export function useDatabaseList( ); } -export function useDatabaseListData( +export function useDatabaseListData( ref: database.Reference | database.Query, - options?: ReactFireOptions -): T { + options?: ReactFireOptions +): T[] { return useObservable( listVal(ref, checkIdField(options)), `RTDB ListData: ${ref.toString()}`, From 9508beeb2621b3a5854b3722ecb56eb3302c2725 Mon Sep 17 00:00:00 2001 From: jhuleatt Date: Tue, 12 Nov 2019 16:40:48 -0800 Subject: [PATCH 5/5] add to docs --- docs/reference.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/docs/reference.md b/docs/reference.md index 79aded94..50eda72e 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -22,10 +22,14 @@ - Database - Cloud Firestore - [`useFirestoreDoc`](#useFirestoreDoc) + - [`useFirestoreDocData`](#useFirestoreDocData) - [`useFirestoreCollection`](#useFirestoreCollection) + - [`useFirestoreCollectionData`](#useFirestoreCollectionData) - Realtime Database - [`useDatabaseObject`](#useDatabaseObject) + - [`useDatabaseObjectData`](#useDatabaseObjectData) - [`useDatabaseList`](#useDatabaseList) + - [`useDatabaseListData`](#useDatabaseListData) - Cloud Storage - [`useStorageTask`](#useStorageTask) - [`useStorageDownloadURL`](#useStorageDownloadURL) @@ -187,6 +191,23 @@ _Throws a Promise by default_ [`DocumentSnapshot`](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentSnapshot) +### `useFirestoreDocData` + +Listen to a Firestore Document. + +_Throws a Promise by default_ + +#### Parameters + +| Parameter | Type | Description | +| ----------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| ref | [`DocumentReference`](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference) | A reference to the document you want to listen to | +| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. | + +#### Returns + +`T` + ### `useFirestoreCollection` Listen to a Firestore Collection. @@ -204,6 +225,23 @@ _Throws a Promise by default_ [`QuerySnapshot`](https://firebase.google.com/docs/reference/js/firebase.firestore.QuerySnapshot) +### `useFirestoreCollectionData` + +Listen to a Firestore Collection. + +_Throws a Promise by default_ + +#### Parameters + +| Parameter | Type | Description | +| ----------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| ref | [`Query`](https://firebase.google.com/docs/reference/js/firebase.firestore.Query) | A query for the collection you want to listen to | +| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. | + +#### Returns + +`T[]` + ### `useDatabaseObject` Listen to a Realtime Database Object. @@ -221,6 +259,23 @@ _Throws a Promise by default_ [`QueryChange`](https://github.com/firebase/firebase-js-sdk/blob/6b53e0058483c9002d2fe56119f86fc9fb96b56c/packages/rxfire/database/interfaces.ts#L28) +### `useDatabaseObjectData` + +Listen to a Realtime Database Object. + +_Throws a Promise by default_ + +#### Parameters + +| Parameter | Type | Description | +| ----------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| ref | [`Reference`](https://firebase.google.com/docs/reference/js/firebase.database.Reference) | A reference to the object you want to listen to | +| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. | + +#### Returns + +`T` + ### `useDatabaseList` Listen to a Realtime Database list. @@ -238,6 +293,23 @@ _Throws a Promise by default_ [`QueryChange[]`](https://github.com/firebase/firebase-js-sdk/blob/6b53e0058483c9002d2fe56119f86fc9fb96b56c/packages/rxfire/database/interfaces.ts#L28) +### `useDatabaseListData` + +Listen to a Realtime Database list. + +_Throws a Promise by default_ + +#### Parameters + +| Parameter | Type | Description | +| ----------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| ref | [`Reference`](https://firebase.google.com/docs/reference/js/firebase.database.Reference) | A reference to the list you want to listen to | +| options _?_ | ReactFireOptions | Options. This hook will not throw a Promise if you provide `startWithValue`. | + +#### Returns + +`T[]` + ### `useStorageTask` Listen to a Storage UploadTask