From fb5380b02aad82c094d1ed5db32e5eab01a058b2 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 7 May 2019 16:46:31 +0100 Subject: [PATCH 01/12] Combine multiple optional parameters into a single `options` parameter --- firestore/index.js.flow | 24 ++++++++++++++++-------- firestore/useCollectionData.ts | 12 +++++++++--- firestore/useCollectionDataOnce.ts | 10 +++++++--- firestore/useDocumentData.ts | 12 +++++++++--- firestore/useDocumentDataOnce.ts | 9 +++++++-- 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/firestore/index.js.flow b/firestore/index.js.flow index c1141ae..4abe47d 100644 --- a/firestore/index.js.flow +++ b/firestore/index.js.flow @@ -40,13 +40,17 @@ declare export function useCollectionOnce( ): CollectionHook; declare export function useCollectionData( query?: Query | null, - options?: SnapshotListenOptions, - idField?: string + options?: { + idField?: string, + snapshotListenOptions?: SnapshotListenOptions, + } ): CollectionDataHook; declare export function useCollectionDataOnce( query?: Query | null, - options?: GetOptions, - idField?: string + options?: { + getOptions?: GetOptions, + idField?: string, + } ): CollectionDataHook; declare export function useDocument( ref?: DocumentReference | null, @@ -58,11 +62,15 @@ declare export function useDocumentOnce( ): DocumentHook; declare export function useDocumentData( ref?: DocumentReference | null, - options?: SnapshotListenOptions, - idField?: string + options?: { + idField?: string, + snapshotListenOptions?: SnapshotListenOptions, + } ): DocumentDataHook; declare export function useDocumentDataOnce( ref?: DocumentReference | null, - options?: GetOptions, - idField?: string + options?: { + getOptions?: GetOptions, + idField?: string, + } ): DocumentDataHook; diff --git a/firestore/useCollectionData.ts b/firestore/useCollectionData.ts index 9f7b23c..dedfe44 100644 --- a/firestore/useCollectionData.ts +++ b/firestore/useCollectionData.ts @@ -10,10 +10,16 @@ export type CollectionDataHook = { export default ( query?: firestore.Query | null, - options?: firestore.SnapshotListenOptions, - idField?: string + options?: { + idField?: string; + snapshotListenOptions?: firestore.SnapshotListenOptions; + } ): CollectionDataHook => { - const { error, loading, value } = useCollection(query, options); + const idField = options ? options.idField : undefined; + const snapshotListenOptions = options + ? options.snapshotListenOptions + : undefined; + const { error, loading, value } = useCollection(query, snapshotListenOptions); return { error, loading, diff --git a/firestore/useCollectionDataOnce.ts b/firestore/useCollectionDataOnce.ts index 203e0e0..b72754e 100644 --- a/firestore/useCollectionDataOnce.ts +++ b/firestore/useCollectionDataOnce.ts @@ -5,10 +5,14 @@ import { CollectionDataHook } from './useCollectionData'; export default ( query?: firestore.Query | null, - options?: firestore.GetOptions, - idField?: string + options?: { + getOptions?: firestore.GetOptions; + idField?: string; + } ): CollectionDataHook => { - const { error, loading, value } = useCollectionOnce(query, options); + const idField = options ? options.idField : undefined; + const getOptions = options ? options.getOptions : undefined; + const { error, loading, value } = useCollectionOnce(query, getOptions); return { error, loading, diff --git a/firestore/useDocumentData.ts b/firestore/useDocumentData.ts index c24c411..3661cb2 100644 --- a/firestore/useDocumentData.ts +++ b/firestore/useDocumentData.ts @@ -10,10 +10,16 @@ export type DocumentDataHook = { export default ( docRef?: firestore.DocumentReference | null, - options?: firestore.SnapshotListenOptions, - idField?: string + options?: { + idField?: string; + snapshotListenOptions?: firestore.SnapshotListenOptions; + } ): DocumentDataHook => { - const { error, loading, value } = useDocument(docRef, options); + const idField = options ? options.idField : undefined; + const snapshotListenOptions = options + ? options.snapshotListenOptions + : undefined; + const { error, loading, value } = useDocument(docRef, snapshotListenOptions); return { error, loading, diff --git a/firestore/useDocumentDataOnce.ts b/firestore/useDocumentDataOnce.ts index 3a83c07..0f47bd6 100644 --- a/firestore/useDocumentDataOnce.ts +++ b/firestore/useDocumentDataOnce.ts @@ -5,9 +5,14 @@ import { DocumentDataHook } from './useDocumentData'; export default ( docRef?: firestore.DocumentReference | null, - idField?: string + options?: { + getOptions?: firestore.GetOptions; + idField?: string; + } ): DocumentDataHook => { - const { error, loading, value } = useDocumentOnce(docRef); + const idField = options ? options.idField : undefined; + const getOptions = options ? options.getOptions : undefined; + const { error, loading, value } = useDocumentOnce(docRef, getOptions); return { error, loading, From b5ea17a69b1cbffb09f078b30f922ec80479da31 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 7 May 2019 16:46:48 +0100 Subject: [PATCH 02/12] Break README into multiple product sections --- README.md | 439 +------------------------------------------- auth/README.md | 48 +++++ database/README.md | 132 +++++++++++++ firestore/README.md | 226 +++++++++++++++++++++++ storage/README.md | 46 +++++ 5 files changed, 456 insertions(+), 435 deletions(-) create mode 100644 auth/README.md create mode 100644 database/README.md create mode 100644 firestore/README.md create mode 100644 storage/README.md diff --git a/README.md b/README.md index c830e23..928ccde 100644 --- a/README.md +++ b/README.md @@ -27,441 +27,10 @@ This library explores how React Hooks can work to make integration with Firebase ## Documentation -- [Auth Hooks](#Auth) -- [Cloud Firestore Hooks](#cloud-firestore) -- [Cloud Storage Hooks](#cloud-storage) -- [Realtime Database Hooks](#realtime-database) - -### Auth - -React Firebase Hooks provides a convenience listener for Firebase Auth's auth state. The hook wraps around the `firebase.auth().onAuthStateChange()` method to ensure that it is always up to date. - -#### `useAuthState(auth)` - -Parameters: - -- `auth`: `firebase.auth.Auth` - -Returns: -`AuthStateHook` containing: - -- `initialising`: If the listener is still waiting for the user to be loaded -- `user`: The `firebase.User`, or `null`, if no user is logged in - -Example: - -```js -import { useAuthState } from 'react-firebase-hooks/auth'; - -const CurrentUser = () => { - const { initialising, user } = useAuthState(firebase.auth()); - const login = () => { - firebase.auth().signInWithEmailAndPassword('test@test.com', 'password'); - }; - const logout = () => { - firebase.auth().signOut(); - }; - - if (initialising) { - return ( -
-

Initialising User...

-
- ); - } - if (user) { - return ( -
-

Current User: {user.email}

- -
- ); - } - return ; -}; -``` - -### Cloud Firestore - -React Firebase Hooks provides convenience listeners for Collections and Documents stored with -Cloud Firestore. The hooks wrap around the `firebase.firestore.collection().onSnapshot()` -and `firebase.firestore().doc().onSnapshot()` methods. - -In addition to returning the snapshot value, the hooks provide an `error` and `loading` property -to give a complete lifecycle for loading and listening to Cloud Firestore. - -There are 2 variants of each hook: - -- `useX` which subscribes to the underlying Collection or Document and listens for changes -- `useXOnce` which reads the current value of the Collection or Document - -#### `useCollection(query, options)` - -Parameters: - -- `query`: `firebase.firestore.Query` -- `options`: `firebase.firestore.SnapshotListenOptions` - -Returns: -`CollectionHook` containing - -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.QuerySnapshot` - -Example: - -```js -import { useCollection } from 'react-firebase-hooks/firestore'; - -const FirestoreCollection = () => { - const { error, loading, value } = useCollection( - firebase.firestore().collection('hooks') - ); - return ( -
-

- {error && Error: {error}} - {loading && Collection: Loading...} - {value && ( - - Collection:{' '} - {value.docs.map(doc => ( - - {JSON.stringify(doc.data())},{' '} - - ))} - - )} -

-
- ); -}; -``` - -#### `useCollectionOnce(query, options)` - -Parameters: - -- `query`: `firebase.firestore.Query` -- `options`: `firebase.firestore.GetOptions` - -Returns: -`CollectionHook` containing - -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.QuerySnapshot` - -Import: - -```js -import { useCollectionOnce } from 'react-firebase-hooks/firestore'; -``` - -#### `useCollectionData(ref, idField)` - -As `useCollection`, but this hook returns a typed list of the -`QuerySnapshot.docs` values, rather than the the `QuerySnapshot` itself. - -Parameters: - -- `query`: `firebase.firestore.Query` -- `options`: (Optional) `firebase.firestore.GetOptions` -- `idField`: (Optional) Name of field that should be populated with the `QuerySnapshot.id` property - -Returns: -`CollectionDataHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.firestore.DocumentSnapshot.data()` values, combined with the optional id field - -Import: - -```js -import { useCollectionData } from 'react-firebase-hooks/firestore'; -``` - -#### `useDocument(docRef)` - -Parameters: - -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) `firebase.firestore.SnapshotListenOptions` - -Returns: -`DocumentHook` containing - -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.DocumentSnapshot` - -Example: - -```js -import { useDocument } from 'react-firebase-hooks/firestore'; - -const FirestoreDocument = () => { - const { error, loading, value } = useDocument( - firebase.firestore().doc('hooks/nBShXiRGFAhuiPfBaGpt') - ); - return ( -
-

- {error && Error: {error}} - {loading && Document: Loading...} - {value && Document: {JSON.stringify(value.data())}} -

-
- ); -}; -``` - -#### `useDocumentOnce(docRef)` - -Parameters: - -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) `firebase.firestore.GetOptions` - -Returns: -`DocumentHook` containing - -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.DocumentSnapshot` - -Import: - -```js -import { useDocumentOnce } from 'react-firebase-hooks/firestore'; -``` - -#### `useDocumentData(ref)` - -As `useDocument`, but this hook returns the typed contents of -`DocumentSnapshot.val()` rather than the `DocumentSnapshot` itself. - -Parameters: - -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) `firebase.firestore.SnapshotListenOptions` -- `idField`: (Optional) Name of field that should be populated with the `DocumentSnapshot.id` property - -Returns: -`DocumentDataHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: The contents of `firebase.firestore.DocumentSnapshot.data()`, combined with the optional id field - -Import: - -```js -import { useDocumentData } from 'react-firebase-hooks/firestore'; -``` - -#### `useDocumentDataOnce(ref)` - -Parameters: - -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) `firebase.firestore.GetOptions` -- `idField`: (Optional) Name of field that should be populated with the `DocumentSnapshot.id` property - -Returns: -`DocumentDataHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: The contents of `firebase.firestore.DocumentSnapshot.data()`, combined with the optional id field - -Import: - -```js -import { useDocumentDataOnce } from 'react-firebase-hooks/firestore'; -``` - -### Cloud Storage - -React Firebase Hooks provides convenience listeners for files stored within -Firebase Cloud Storage. The hooks wrap around the `firebase.storage().ref().getDownloadURL()` method. - -In addition to returning the download URL, the hooks provide an `error` and `loading` property -to give a complete lifecycle for loading from Cloud Storage. - -#### `useDownloadURL(ref)` - -Parameters: - -- `ref`: `firebase.storage.Reference` - -Returns: -`DownloadURLHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the download URL is still being loaded -- `value`: The download URL - -Example: - -```js -import { useDownloadURL } from 'react-firebase-hooks/storage'; - -const DownloadURL = () => { - const { error, loading, value } = useDownloadURL( - firebase.storage().ref('path/to/file') - ); - - return ( -
-

- {error && Error: {error}} - {loading && Download URL: Loading...} - {!loading && value && ( - - Download URL: {value} - - )} -

-
- ); -}; -``` - -### Realtime Database - -React Firebase Hooks provides convenience listeners for lists and values stored within the -Firebase Realtime Database. The hooks wrap around the `firebase.database().ref().on()` method. - -In addition to returning the list or value, the hooks provide an `error` and `loading` property -to give a complete lifecycle for loading and listening to the Realtime Database. - -#### `useList(ref)` - -Parameters: - -- `ref`: `firebase.database.Reference` - -Returns: -`ListHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.database.DataSnapshot` - -Example: - -```js -import { useList } from 'react-firebase-hooks/database'; - -const DatabaseList = () => { - const { error, loading, value } = useList(firebase.database().ref('list')); - - return ( -
-

- {error && Error: {error}} - {loading && List: Loading...} - {!loading && value && ( - - - List:{' '} - {value.map(v => ( - {v.val()}, - ))} - - - )} -

-
- ); -}; -``` - -#### `useListKeys(ref)` - -As above, but this hook returns a list of the `DataSnapshot.key` values, rather than the the -`DataSnapshot`s themselves. - -Parameters: - -- `ref`: `firebase.database.Reference` - -Returns: -`ListKeysHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.database.DataSnapshot.key` values - -#### `useListVals(ref, keyField)` - -Similar to `useList`, but this hook returns a typed list of the `DataSnapshot.val()` values, rather than the the -`DataSnapshot`s themselves. - -Parameters: - -- `ref`: `firebase.database.Reference` -- `keyField`: (Optional) Name of field that should be populated with the `DataSnapshot.key` property - -Returns: -`ListValsHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.database.DataSnapshot.val()` values, combined with the optional key field - -#### `useObject(ref)` - -Parameters: - -- `ref`: `firebase.database.Reference` - -Returns: -`ObjectHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.database.DataSnapshot` - -Example: - -```js -import { useObject } from 'react-firebase-hooks/database'; - -const DatabaseValue = () => { - const { error, loading, value } = useObject(firebase.database().ref('value')); - - return ( -
-

- {error && Error: {error}} - {loading && Value: Loading...} - {value && Value: {value.val()}} -

-
- ); -}; -``` - -#### `useObjectVal(ref)` - -As above, but this hook returns the typed contents of `DataSnapshot.val()` rather than the -`DataSnapshot` itself. - -Parameters: - -- `ref`: `firebase.database.Reference` -- `keyField`: (Optional) Name of field that should be populated with the `DataSnapshot.key` property - -Returns: -`ObjectValHook` containing - -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: The contents of `firebase.database.DataSnapshot.val()`, combined with the optional key field +- [Auth Hooks](auth/README.md) +- [Cloud Firestore Hooks](firestore/README.md) +- [Cloud Storage Hooks](storage/README.md) +- [Realtime Database Hooks](database/README.md) ## License diff --git a/auth/README.md b/auth/README.md new file mode 100644 index 0000000..b649aaf --- /dev/null +++ b/auth/README.md @@ -0,0 +1,48 @@ +# React Firebase Hooks - Auth + +React Firebase Hooks provides a convenience listener for Firebase Auth's auth state. The hook wraps around the `firebase.auth().onAuthStateChange()` method to ensure that it is always up to date. + +## `useAuthState(auth)` + +Parameters: + +- `auth`: `firebase.auth.Auth` + +Returns: +`AuthStateHook` containing: + +- `initialising`: If the listener is still waiting for the user to be loaded +- `user`: The `firebase.User`, or `null`, if no user is logged in + +### Example + +```js +import { useAuthState } from 'react-firebase-hooks/auth'; + +const CurrentUser = () => { + const { initialising, user } = useAuthState(firebase.auth()); + const login = () => { + firebase.auth().signInWithEmailAndPassword('test@test.com', 'password'); + }; + const logout = () => { + firebase.auth().signOut(); + }; + + if (initialising) { + return ( +
+

Initialising User...

+
+ ); + } + if (user) { + return ( +
+

Current User: {user.email}

+ +
+ ); + } + return ; +}; +``` diff --git a/database/README.md b/database/README.md new file mode 100644 index 0000000..fef3eab --- /dev/null +++ b/database/README.md @@ -0,0 +1,132 @@ +# React Firebase Hooks - Realtime Database + +React Firebase Hooks provides convenience listeners for lists and values stored within the +Firebase Realtime Database. The hooks wrap around the `firebase.database().ref().on()` method. + +In addition to returning the list or value, the hooks provide an `error` and `loading` property +to give a complete lifecycle for loading and listening to the Realtime Database. + +## `useList(ref)` + +Parameters: + +- `ref`: `firebase.database.Reference` + +Returns: +`ListHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A list of `firebase.database.DataSnapshot` + +### Example + +```js +import { useList } from 'react-firebase-hooks/database'; + +const DatabaseList = () => { + const { error, loading, value } = useList(firebase.database().ref('list')); + + return ( +
+

+ {error && Error: {error}} + {loading && List: Loading...} + {!loading && value && ( + + + List:{' '} + {value.map(v => ( + {v.val()}, + ))} + + + )} +

+
+ ); +}; +``` + +## `useListKeys(ref)` + +As above, but this hook returns a list of the `DataSnapshot.key` values, rather than the the +`DataSnapshot`s themselves. + +Parameters: + +- `ref`: `firebase.database.Reference` + +Returns: +`ListKeysHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A list of `firebase.database.DataSnapshot.key` values + +## `useListVals(ref, keyField)` + +Similar to `useList`, but this hook returns a typed list of the `DataSnapshot.val()` values, rather than the the +`DataSnapshot`s themselves. + +Parameters: + +- `ref`: `firebase.database.Reference` +- `keyField`: (Optional) Name of field that should be populated with the `DataSnapshot.key` property + +Returns: +`ListValsHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A list of `firebase.database.DataSnapshot.val()` values, combined with the optional key field + +## `useObject(ref)` + +Parameters: + +- `ref`: `firebase.database.Reference` + +Returns: +`ObjectHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A `firebase.database.DataSnapshot` + +### Example + +```js +import { useObject } from 'react-firebase-hooks/database'; + +const DatabaseValue = () => { + const { error, loading, value } = useObject(firebase.database().ref('value')); + + return ( +
+

+ {error && Error: {error}} + {loading && Value: Loading...} + {value && Value: {value.val()}} +

+
+ ); +}; +``` + +## `useObjectVal(ref)` + +As above, but this hook returns the typed contents of `DataSnapshot.val()` rather than the +`DataSnapshot` itself. + +Parameters: + +- `ref`: `firebase.database.Reference` +- `keyField`: (Optional) Name of field that should be populated with the `DataSnapshot.key` property + +Returns: +`ObjectValHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: The contents of `firebase.database.DataSnapshot.val()`, combined with the optional key field diff --git a/firestore/README.md b/firestore/README.md new file mode 100644 index 0000000..4da7113 --- /dev/null +++ b/firestore/README.md @@ -0,0 +1,226 @@ +# React Firebase Hooks - Cloud Firestore + +React Firebase Hooks provides convenience listeners for Collections and Documents stored with +Cloud Firestore. The hooks wrap around the `firebase.firestore.collection().onSnapshot()` +and `firebase.firestore().doc().onSnapshot()` methods. + +In addition to returning the snapshot value, the hooks provide an `error` and `loading` property +to give a complete lifecycle for loading and listening to Cloud Firestore. + +There are 2 variants of each hook: + +- `useX` which subscribes to the underlying Collection or Document and listens for changes +- `useXOnce` which reads the current value of the Collection or Document + +## `useCollection(query, options)` + +Parameters: + +- `query`: `firebase.firestore.Query` +- `options`: `firebase.firestore.SnapshotListenOptions` + +Returns: +`CollectionHook` containing + +- `error`: An optional `firebase.FirebaseError` returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A `firebase.firestore.QuerySnapshot` + +### Example + +```js +import { useCollection } from 'react-firebase-hooks/firestore'; + +const FirestoreCollection = () => { + const { error, loading, value } = useCollection( + firebase.firestore().collection('hooks') + ); + return ( +
+

+ {error && Error: {error}} + {loading && Collection: Loading...} + {value && ( + + Collection:{' '} + {value.docs.map(doc => ( + + {JSON.stringify(doc.data())},{' '} + + ))} + + )} +

+
+ ); +}; +``` + +## `useCollectionOnce(query, options)` + +Parameters: + +- `query`: `firebase.firestore.Query` +- `options`: `firebase.firestore.GetOptions` + +Returns: +`CollectionHook` containing + +- `error`: An optional `firebase.FirebaseError` returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A `firebase.firestore.QuerySnapshot` + +Import: + +```js +import { useCollectionOnce } from 'react-firebase-hooks/firestore'; +``` + +## `useCollectionData(ref, idField)` + +As `useCollection`, but this hook returns a typed list of the +`QuerySnapshot.docs` values, rather than the the `QuerySnapshot` itself. + +Parameters: + +- `query`: `firebase.firestore.Query` +- `options`: (Optional) Object containing: + - `snapshotListenOptions`: (Optional) `firebase.firestore.SnapshotListenOptions` + - `idField`: (Optional) Name of field that should be populated with the `QuerySnapshot.id` property + +Returns: +`CollectionDataHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A list of `firebase.firestore.DocumentSnapshot.data()` values, combined with the optional id field + +Import: + +```js +import { useCollectionData } from 'react-firebase-hooks/firestore'; +``` + +## `useCollectionDataOnce(ref, idField)` + +Parameters: + +- `query`: `firebase.firestore.Query` +- `options`: (Optional) Object containing: + - `getOptions`: (Optional) `firebase.firestore.GetOptions` + - `idField`: (Optional) Name of field that should be populated with the `QuerySnapshot.id` property + +Returns: +`CollectionDataHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A list of `firebase.firestore.DocumentSnapshot.data()` values, combined with the optional id field + +Import: + +```js +import { useCollectionDataOnce } from 'react-firebase-hooks/firestore'; +``` + +## `useDocument(docRef)` + +Parameters: + +- `docRef`: `firebase.firestore.DocumentReference` +- `options`: (Optional) `firebase.firestore.SnapshotListenOptions` + +Returns: +`DocumentHook` containing + +- `error`: An optional `firebase.FirebaseError` returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A `firebase.firestore.DocumentSnapshot` + +### Example + +```js +import { useDocument } from 'react-firebase-hooks/firestore'; + +const FirestoreDocument = () => { + const { error, loading, value } = useDocument( + firebase.firestore().doc('hooks/nBShXiRGFAhuiPfBaGpt') + ); + return ( +
+

+ {error && Error: {error}} + {loading && Document: Loading...} + {value && Document: {JSON.stringify(value.data())}} +

+
+ ); +}; +``` + +## `useDocumentOnce(docRef)` + +Parameters: + +- `docRef`: `firebase.firestore.DocumentReference` +- `options`: (Optional) `firebase.firestore.GetOptions` + +Returns: +`DocumentHook` containing + +- `error`: An optional `firebase.FirebaseError` returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: A `firebase.firestore.DocumentSnapshot` + +Import: + +```js +import { useDocumentOnce } from 'react-firebase-hooks/firestore'; +``` + +## `useDocumentData(ref)` + +As `useDocument`, but this hook returns the typed contents of +`DocumentSnapshot.val()` rather than the `DocumentSnapshot` itself. + +Parameters: + +- `docRef`: `firebase.firestore.DocumentReference` +- `options`: (Optional) Object containing: + - `snapshotListenOptions`: (Optional) `firebase.firestore.SnapshotListenOptions` + - `idField`: (Optional) Name of field that should be populated with the `DocumentSnapshot.id` property + +Returns: +`DocumentDataHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: The contents of `firebase.firestore.DocumentSnapshot.data()`, combined with the optional id field + +Import: + +```js +import { useDocumentData } from 'react-firebase-hooks/firestore'; +``` + +## `useDocumentDataOnce(ref)` + +Parameters: + +- `docRef`: `firebase.firestore.DocumentReference` +- `options`: (Optional) Object containing: + - `getOptions`: (Optional) `firebase.firestore.GetOptions` + - `idField`: (Optional) Name of field that should be populated with the `DocumentSnapshot.id` property + +Returns: +`DocumentDataHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the listener is still being loaded +- `value`: The contents of `firebase.firestore.DocumentSnapshot.data()`, combined with the optional id field + +Import: + +```js +import { useDocumentDataOnce } from 'react-firebase-hooks/firestore'; +``` diff --git a/storage/README.md b/storage/README.md new file mode 100644 index 0000000..a037d59 --- /dev/null +++ b/storage/README.md @@ -0,0 +1,46 @@ +# Cloud Storage + +React Firebase Hooks provides convenience listeners for files stored within +Firebase Cloud Storage. The hooks wrap around the `firebase.storage().ref().getDownloadURL()` method. + +In addition to returning the download URL, the hooks provide an `error` and `loading` property +to give a complete lifecycle for loading from Cloud Storage. + +## `useDownloadURL(ref)` + +Parameters: + +- `ref`: `firebase.storage.Reference` + +Returns: +`DownloadURLHook` containing + +- `error`: An optional error object returned by Firebase +- `loading`: A `boolean` to indicate if the download URL is still being loaded +- `value`: The download URL + +### Example + +```js +import { useDownloadURL } from 'react-firebase-hooks/storage'; + +const DownloadURL = () => { + const { error, loading, value } = useDownloadURL( + firebase.storage().ref('path/to/file') + ); + + return ( +
+

+ {error && Error: {error}} + {loading && Download URL: Loading...} + {!loading && value && ( + + Download URL: {value} + + )} +

+
+ ); +}; +``` From 4549aff53151f0142b3e40f49c32004f7f71034d Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 7 May 2019 16:53:33 +0100 Subject: [PATCH 03/12] Add list of hooks to each documentation page --- auth/README.md | 2 ++ database/README.md | 6 ++++++ firestore/README.md | 9 +++++++++ storage/README.md | 2 ++ 4 files changed, 19 insertions(+) diff --git a/auth/README.md b/auth/README.md index b649aaf..58e6f15 100644 --- a/auth/README.md +++ b/auth/README.md @@ -2,6 +2,8 @@ React Firebase Hooks provides a convenience listener for Firebase Auth's auth state. The hook wraps around the `firebase.auth().onAuthStateChange()` method to ensure that it is always up to date. +- [useAuthState](#useauthstateauth) + ## `useAuthState(auth)` Parameters: diff --git a/database/README.md b/database/README.md index fef3eab..4a14e46 100644 --- a/database/README.md +++ b/database/README.md @@ -6,6 +6,12 @@ Firebase Realtime Database. The hooks wrap around the `firebase.database().ref() In addition to returning the list or value, the hooks provide an `error` and `loading` property to give a complete lifecycle for loading and listening to the Realtime Database. +- [useList](#uselistref) +- [useListKeys](#uselistkeystref) +- [useListVals](#uselistvalstref-keyfield) +- [useObject](#useobjectref) +- [useObjectVal](#useobjectvaltref) + ## `useList(ref)` Parameters: diff --git a/firestore/README.md b/firestore/README.md index 4da7113..0b4aa28 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -12,6 +12,15 @@ There are 2 variants of each hook: - `useX` which subscribes to the underlying Collection or Document and listens for changes - `useXOnce` which reads the current value of the Collection or Document +- [useCollection](#usecollectionquery-options) +- [useCollectionOnce](#usecollectiononcequery-options) +- [useCollectionData](#usecollectiondatatref-idfield) +- [useCollectionDataOnce](#usecollectiondataoncetref-idfield) +- [useDocument](#usedocumentdocref) +- [useDocumentOnce](#usedocumentoncedocref) +- [useDocumentData](#usedocumentdatatref) +- [useDocumentDataOnce](#usedocumentdataoncetref) + ## `useCollection(query, options)` Parameters: diff --git a/storage/README.md b/storage/README.md index a037d59..5b33154 100644 --- a/storage/README.md +++ b/storage/README.md @@ -6,6 +6,8 @@ Firebase Cloud Storage. The hooks wrap around the `firebase.storage().ref().getD In addition to returning the download URL, the hooks provide an `error` and `loading` property to give a complete lifecycle for loading from Cloud Storage. +- [useDownloadURL](#usedownloadurlref) + ## `useDownloadURL(ref)` Parameters: From 97b86bd0ff5c26372430e0d304dcdb21d7801a28 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 7 May 2019 16:55:01 +0100 Subject: [PATCH 04/12] Tweak header sizes --- auth/README.md | 4 ++-- database/README.md | 14 +++++++------- firestore/README.md | 20 ++++++++++---------- storage/README.md | 4 ++-- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/auth/README.md b/auth/README.md index 58e6f15..6c2d3d8 100644 --- a/auth/README.md +++ b/auth/README.md @@ -4,7 +4,7 @@ React Firebase Hooks provides a convenience listener for Firebase Auth's auth st - [useAuthState](#useauthstateauth) -## `useAuthState(auth)` +### `useAuthState(auth)` Parameters: @@ -16,7 +16,7 @@ Returns: - `initialising`: If the listener is still waiting for the user to be loaded - `user`: The `firebase.User`, or `null`, if no user is logged in -### Example +#### Example ```js import { useAuthState } from 'react-firebase-hooks/auth'; diff --git a/database/README.md b/database/README.md index 4a14e46..f0c0b42 100644 --- a/database/README.md +++ b/database/README.md @@ -12,7 +12,7 @@ to give a complete lifecycle for loading and listening to the Realtime Database. - [useObject](#useobjectref) - [useObjectVal](#useobjectvaltref) -## `useList(ref)` +### `useList(ref)` Parameters: @@ -25,7 +25,7 @@ Returns: - `loading`: A `boolean` to indicate if the listener is still being loaded - `value`: A list of `firebase.database.DataSnapshot` -### Example +#### Example ```js import { useList } from 'react-firebase-hooks/database'; @@ -54,7 +54,7 @@ const DatabaseList = () => { }; ``` -## `useListKeys(ref)` +### `useListKeys(ref)` As above, but this hook returns a list of the `DataSnapshot.key` values, rather than the the `DataSnapshot`s themselves. @@ -70,7 +70,7 @@ Returns: - `loading`: A `boolean` to indicate if the listener is still being loaded - `value`: A list of `firebase.database.DataSnapshot.key` values -## `useListVals(ref, keyField)` +### `useListVals(ref, keyField)` Similar to `useList`, but this hook returns a typed list of the `DataSnapshot.val()` values, rather than the the `DataSnapshot`s themselves. @@ -87,7 +87,7 @@ Returns: - `loading`: A `boolean` to indicate if the listener is still being loaded - `value`: A list of `firebase.database.DataSnapshot.val()` values, combined with the optional key field -## `useObject(ref)` +### `useObject(ref)` Parameters: @@ -100,7 +100,7 @@ Returns: - `loading`: A `boolean` to indicate if the listener is still being loaded - `value`: A `firebase.database.DataSnapshot` -### Example +#### Example ```js import { useObject } from 'react-firebase-hooks/database'; @@ -120,7 +120,7 @@ const DatabaseValue = () => { }; ``` -## `useObjectVal(ref)` +### `useObjectVal(ref)` As above, but this hook returns the typed contents of `DataSnapshot.val()` rather than the `DataSnapshot` itself. diff --git a/firestore/README.md b/firestore/README.md index 0b4aa28..d13a17b 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -21,7 +21,7 @@ There are 2 variants of each hook: - [useDocumentData](#usedocumentdatatref) - [useDocumentDataOnce](#usedocumentdataoncetref) -## `useCollection(query, options)` +### `useCollection(query, options)` Parameters: @@ -35,7 +35,7 @@ Returns: - `loading`: A `boolean` to indicate if the listener is still being loaded - `value`: A `firebase.firestore.QuerySnapshot` -### Example +#### Example ```js import { useCollection } from 'react-firebase-hooks/firestore'; @@ -65,7 +65,7 @@ const FirestoreCollection = () => { }; ``` -## `useCollectionOnce(query, options)` +### `useCollectionOnce(query, options)` Parameters: @@ -85,7 +85,7 @@ Import: import { useCollectionOnce } from 'react-firebase-hooks/firestore'; ``` -## `useCollectionData(ref, idField)` +### `useCollectionData(ref, idField)` As `useCollection`, but this hook returns a typed list of the `QuerySnapshot.docs` values, rather than the the `QuerySnapshot` itself. @@ -110,7 +110,7 @@ Import: import { useCollectionData } from 'react-firebase-hooks/firestore'; ``` -## `useCollectionDataOnce(ref, idField)` +### `useCollectionDataOnce(ref, idField)` Parameters: @@ -132,7 +132,7 @@ Import: import { useCollectionDataOnce } from 'react-firebase-hooks/firestore'; ``` -## `useDocument(docRef)` +### `useDocument(docRef)` Parameters: @@ -146,7 +146,7 @@ Returns: - `loading`: A `boolean` to indicate if the listener is still being loaded - `value`: A `firebase.firestore.DocumentSnapshot` -### Example +#### Example ```js import { useDocument } from 'react-firebase-hooks/firestore'; @@ -167,7 +167,7 @@ const FirestoreDocument = () => { }; ``` -## `useDocumentOnce(docRef)` +### `useDocumentOnce(docRef)` Parameters: @@ -187,7 +187,7 @@ Import: import { useDocumentOnce } from 'react-firebase-hooks/firestore'; ``` -## `useDocumentData(ref)` +### `useDocumentData(ref)` As `useDocument`, but this hook returns the typed contents of `DocumentSnapshot.val()` rather than the `DocumentSnapshot` itself. @@ -212,7 +212,7 @@ Import: import { useDocumentData } from 'react-firebase-hooks/firestore'; ``` -## `useDocumentDataOnce(ref)` +### `useDocumentDataOnce(ref)` Parameters: diff --git a/storage/README.md b/storage/README.md index 5b33154..2c5ea4e 100644 --- a/storage/README.md +++ b/storage/README.md @@ -8,7 +8,7 @@ to give a complete lifecycle for loading from Cloud Storage. - [useDownloadURL](#usedownloadurlref) -## `useDownloadURL(ref)` +### `useDownloadURL(ref)` Parameters: @@ -21,7 +21,7 @@ Returns: - `loading`: A `boolean` to indicate if the download URL is still being loaded - `value`: The download URL -### Example +#### Example ```js import { useDownloadURL } from 'react-firebase-hooks/storage'; From 9073d366d175d4c84779a9ae467500c1543df752 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 7 May 2019 16:57:52 +0100 Subject: [PATCH 05/12] More documentation tweaks --- auth/README.md | 2 ++ database/README.md | 2 ++ firestore/README.md | 2 ++ storage/README.md | 4 +++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/auth/README.md b/auth/README.md index 6c2d3d8..ea55255 100644 --- a/auth/README.md +++ b/auth/README.md @@ -2,6 +2,8 @@ React Firebase Hooks provides a convenience listener for Firebase Auth's auth state. The hook wraps around the `firebase.auth().onAuthStateChange()` method to ensure that it is always up to date. +List of Auth hooks: + - [useAuthState](#useauthstateauth) ### `useAuthState(auth)` diff --git a/database/README.md b/database/README.md index f0c0b42..626432e 100644 --- a/database/README.md +++ b/database/README.md @@ -6,6 +6,8 @@ Firebase Realtime Database. The hooks wrap around the `firebase.database().ref() In addition to returning the list or value, the hooks provide an `error` and `loading` property to give a complete lifecycle for loading and listening to the Realtime Database. +List of Realtime Database hooks: + - [useList](#uselistref) - [useListKeys](#uselistkeystref) - [useListVals](#uselistvalstref-keyfield) diff --git a/firestore/README.md b/firestore/README.md index d13a17b..bef58fd 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -12,6 +12,8 @@ There are 2 variants of each hook: - `useX` which subscribes to the underlying Collection or Document and listens for changes - `useXOnce` which reads the current value of the Collection or Document +List of Cloud Firestore hooks: + - [useCollection](#usecollectionquery-options) - [useCollectionOnce](#usecollectiononcequery-options) - [useCollectionData](#usecollectiondatatref-idfield) diff --git a/storage/README.md b/storage/README.md index 2c5ea4e..69e75e8 100644 --- a/storage/README.md +++ b/storage/README.md @@ -1,4 +1,4 @@ -# Cloud Storage +# React Firebase Hooks - Cloud Storage React Firebase Hooks provides convenience listeners for files stored within Firebase Cloud Storage. The hooks wrap around the `firebase.storage().ref().getDownloadURL()` method. @@ -6,6 +6,8 @@ Firebase Cloud Storage. The hooks wrap around the `firebase.storage().ref().getD In addition to returning the download URL, the hooks provide an `error` and `loading` property to give a complete lifecycle for loading from Cloud Storage. +List of Cloud Storage hooks: + - [useDownloadURL](#usedownloadurlref) ### `useDownloadURL(ref)` From e2ebe99b66df6bf77d930df3d4c7a1c3f0282848 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 7 May 2019 16:59:23 +0100 Subject: [PATCH 06/12] Update documentation links --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 928ccde..5135d65 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ This library explores how React Hooks can work to make integration with Firebase ## Documentation -- [Auth Hooks](auth/README.md) -- [Cloud Firestore Hooks](firestore/README.md) -- [Cloud Storage Hooks](storage/README.md) -- [Realtime Database Hooks](database/README.md) +- [Auth Hooks](/auth) +- [Cloud Firestore Hooks](/firestore) +- [Cloud Storage Hooks](/storage) +- [Realtime Database Hooks](/database) ## License From 378db893ee54a846f64d4e471a523c108913f86b Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Thu, 16 May 2019 11:18:25 +0100 Subject: [PATCH 07/12] Return arrays rather than objects from all hooks --- auth/README.md | 33 +++-- auth/helpers/index.ts | 10 ++ auth/index.js.flow | 9 +- auth/useAuthState.ts | 21 ++- database/README.md | 108 +++++++------- database/helpers/useListReducer.ts | 195 ++++++++++++++++++++++++ database/index.js.flow | 32 +--- database/index.ts | 19 ++- database/useList.ts | 227 ++++------------------------ database/useListKeys.ts | 17 --- database/useListVals.ts | 23 --- database/useObject.ts | 26 ++-- database/useObjectVal.ts | 21 --- firestore/README.md | 231 +++++++++++++---------------- firestore/index.js.flow | 26 +--- firestore/index.ts | 30 ++-- firestore/useCollection.ts | 41 +++-- firestore/useCollectionData.ts | 30 ---- firestore/useCollectionDataOnce.ts | 23 --- firestore/useCollectionOnce.ts | 39 +++-- firestore/useDocument.ts | 39 +++-- firestore/useDocumentData.ts | 28 ---- firestore/useDocumentDataOnce.ts | 21 --- firestore/useDocumentOnce.ts | 37 +++-- package.json | 2 +- storage/README.md | 27 ++-- storage/index.js.flow | 8 +- storage/useDownloadURL.ts | 14 +- util/index.ts | 2 + 29 files changed, 624 insertions(+), 715 deletions(-) create mode 100644 auth/helpers/index.ts create mode 100644 database/helpers/useListReducer.ts delete mode 100644 database/useListKeys.ts delete mode 100644 database/useListVals.ts delete mode 100644 database/useObjectVal.ts delete mode 100644 firestore/useCollectionData.ts delete mode 100644 firestore/useCollectionDataOnce.ts delete mode 100644 firestore/useDocumentData.ts delete mode 100644 firestore/useDocumentDataOnce.ts diff --git a/auth/README.md b/auth/README.md index ea55255..7e15067 100644 --- a/auth/README.md +++ b/auth/README.md @@ -2,29 +2,35 @@ React Firebase Hooks provides a convenience listener for Firebase Auth's auth state. The hook wraps around the `firebase.auth().onAuthStateChange()` method to ensure that it is always up to date. +All hooks can be imported from `react-firebase-hooks/auth`, e.g. + +``` +import { useAuthState } from 'react-firebase-hooks/auth'; +``` + List of Auth hooks: -- [useAuthState](#useauthstateauth) +- [useAuthState](#useauthstate) -### `useAuthState(auth)` +### useAuthState -Parameters: +``` +const [user, loading, error] = useAuthState(auth); +``` -- `auth`: `firebase.auth.Auth` +Returns the `firebase.User` (if logged in), a boolean to indicate whether the the user is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the user. -Returns: -`AuthStateHook` containing: +The `useAuthState` hook takes the following parameters: -- `initialising`: If the listener is still waiting for the user to be loaded -- `user`: The `firebase.User`, or `null`, if no user is logged in +- `auth`: `firebase.auth.Auth` instance for the app you would like to monitor -#### Example +#### Full Example ```js import { useAuthState } from 'react-firebase-hooks/auth'; const CurrentUser = () => { - const { initialising, user } = useAuthState(firebase.auth()); + const [user, initialising, error] = useAuthState(firebase.auth()); const login = () => { firebase.auth().signInWithEmailAndPassword('test@test.com', 'password'); }; @@ -39,6 +45,13 @@ const CurrentUser = () => { ); } + if (error) { + return ( +
+

Error: {error}>/p> +

+ ) + } if (user) { return (
diff --git a/auth/helpers/index.ts b/auth/helpers/index.ts new file mode 100644 index 0000000..2795804 --- /dev/null +++ b/auth/helpers/index.ts @@ -0,0 +1,10 @@ +import { auth, FirebaseError } from 'firebase'; + +export const transformError = (error: auth.Error): FirebaseError => { + return { + message: error.message, + stack: '', + name: '', + code: error.code, + }; +}; diff --git a/auth/index.js.flow b/auth/index.js.flow index 0aaebe5..16476a3 100644 --- a/auth/index.js.flow +++ b/auth/index.js.flow @@ -1,8 +1,9 @@ // @flow import type { FirebaseUser as User } from 'firebase'; +import typeof { FirebaseError } from 'firebase'; import type { Auth } from 'firebase/auth'; -export type AuthStateHook = { - user?: User, - initialising: boolean, -}; + +type LoadingHook = [T | void, boolean, FirebaseError | void]; + +export type AuthStateHook = LoadingHook; declare export function useAuthState(auth: Auth): AuthStateHook; diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index 1a68db3..9969c0d 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -1,20 +1,20 @@ -import { auth, User } from 'firebase'; +import { auth, FirebaseError, User } from 'firebase'; import { useEffect } from 'react'; -import useLoadingValue from '../util/useLoadingValue'; +import { transformError } from './helpers'; +import { LoadingHook, useLoadingValue } from '../util'; -export type AuthStateHook = { - user?: firebase.User; - initialising: boolean; -}; +export type AuthStateHook = LoadingHook; export default (auth: auth.Auth): AuthStateHook => { - const { loading, setValue, value } = useLoadingValue( + const { error, loading, setError, setValue, value } = useLoadingValue( () => auth.currentUser ); useEffect( () => { - const listener = auth.onAuthStateChanged(setValue); + const listener = auth.onAuthStateChanged(setValue, (error: auth.Error) => + setError(transformError(error)) + ); return () => { listener(); @@ -23,8 +23,5 @@ export default (auth: auth.Auth): AuthStateHook => { [auth] ); - return { - initialising: loading, - user: value, - }; + return [value, loading, error]; }; diff --git a/database/README.md b/database/README.md index 626432e..9329272 100644 --- a/database/README.md +++ b/database/README.md @@ -6,34 +6,39 @@ Firebase Realtime Database. The hooks wrap around the `firebase.database().ref() In addition to returning the list or value, the hooks provide an `error` and `loading` property to give a complete lifecycle for loading and listening to the Realtime Database. +All hooks can be imported from `react-firebase-hooks/database`, e.g. + +``` +import { useList } from 'react-firebase-hooks/database'; +``` + List of Realtime Database hooks: - [useList](#uselistref) -- [useListKeys](#uselistkeystref) -- [useListVals](#uselistvalstref-keyfield) +- [useListKeys](#uselistkeyst) +- [useListVals](#uselistvals) - [useObject](#useobjectref) -- [useObjectVal](#useobjectvaltref) +- [useObjectVal](#useobjectval) -### `useList(ref)` +### useList -Parameters: +``` +const [snapshots, loading, error] = useList(reference); +``` -- `ref`: `firebase.database.Reference` +Returns an array of `firebase.database.DataSnapshot` (if a reference is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. -Returns: -`ListHook` containing +The `useList` hook takes the following parameters: -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.database.DataSnapshot` +- `reference`: (optional) `firebase.database.Reference` for the data you would like to load -#### Example +#### Full Example ```js import { useList } from 'react-firebase-hooks/database'; const DatabaseList = () => { - const { error, loading, value } = useList(firebase.database().ref('list')); + const [snapshots, loading, error] = useList(firebase.database().ref('list')); return (
@@ -56,59 +61,51 @@ const DatabaseList = () => { }; ``` -### `useListKeys(ref)` +### useListKeys -As above, but this hook returns a list of the `DataSnapshot.key` values, rather than the the -`DataSnapshot`s themselves. +``` +const [keys, loading, error] = useListKeys(reference); +``` -Parameters: +As `useList`, but this hook returns a list of the `firebase.database.DataSnapshot.key` values, rather than the the `firebase.database.DataSnapshot`s themselves. -- `ref`: `firebase.database.Reference` +The `useListKeys` hook takes the following parameters: -Returns: -`ListKeysHook` containing +- `reference`: (optional) `firebase.database.Reference` for the data you would like to load -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.database.DataSnapshot.key` values +### useListVals -### `useListVals(ref, keyField)` +``` +const [values, loading, error] = useListVals(reference, keyField); +``` -Similar to `useList`, but this hook returns a typed list of the `DataSnapshot.val()` values, rather than the the +As `useList`, but this hook returns a typed list of the `firebase.database.DataSnapshot.val()` values, rather than the the `DataSnapshot`s themselves. -Parameters: - -- `ref`: `firebase.database.Reference` -- `keyField`: (Optional) Name of field that should be populated with the `DataSnapshot.key` property - -Returns: -`ListValsHook` containing +The `useListVals` hook takes the following parameters: -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.database.DataSnapshot.val()` values, combined with the optional key field +- `reference`: (optional) `firebase.database.Reference` for the data you would like to load +- `keyField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.key` property in the returned value. -### `useObject(ref)` +### useObject -Parameters: +``` +const [snapshot, loading, error] = useObject(reference); +``` -- `ref`: `firebase.database.Reference` +Returns a `firebase.database.DataSnapshot` (if a reference is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. -Returns: -`ObjectHook` containing +The `useObject` hook takes the following parameters: -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.database.DataSnapshot` +- `reference`: (optional) `firebase.database.Reference` for the data you would like to load -#### Example +#### Full Example ```js import { useObject } from 'react-firebase-hooks/database'; const DatabaseValue = () => { - const { error, loading, value } = useObject(firebase.database().ref('value')); + const [value, loading, error] = useObject(firebase.database().ref('value')); return (
@@ -122,19 +119,16 @@ const DatabaseValue = () => { }; ``` -### `useObjectVal(ref)` +### useObjectVal -As above, but this hook returns the typed contents of `DataSnapshot.val()` rather than the -`DataSnapshot` itself. - -Parameters: +``` +const [value, loading, error] = useObjectVal(reference, keyField); +``` -- `ref`: `firebase.database.Reference` -- `keyField`: (Optional) Name of field that should be populated with the `DataSnapshot.key` property +As `useObject`, but this hook returns the typed contents of `DataSnapshot.val()` rather than the +`DataSnapshot` itself. -Returns: -`ObjectValHook` containing +The `useObjectVal` hook takes the following parameters: -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: The contents of `firebase.database.DataSnapshot.val()`, combined with the optional key field +- `reference`: (optional) `firebase.database.Reference` for the data you would like to load +- `keyField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.key` property in the returned value. diff --git a/database/helpers/useListReducer.ts b/database/helpers/useListReducer.ts new file mode 100644 index 0000000..8ed4e7d --- /dev/null +++ b/database/helpers/useListReducer.ts @@ -0,0 +1,195 @@ +import { database, FirebaseError } from 'firebase'; +import { useReducer } from 'react'; + +type KeyValueState = { + keys?: string[]; + values?: database.DataSnapshot[]; +}; + +type ReducerState = { + error?: FirebaseError; + loading: boolean; + value: KeyValueState; +}; + +type AddAction = { + type: 'add'; + previousKey?: string | null; + snapshot: database.DataSnapshot | null; +}; +type ChangeAction = { + type: 'change'; + snapshot: database.DataSnapshot | null; +}; +type EmptyAction = { type: 'empty' }; +type ErrorAction = { type: 'error'; error: FirebaseError }; +type MoveAction = { + type: 'move'; + previousKey?: string | null; + snapshot: database.DataSnapshot | null; +}; +type RemoveAction = { + type: 'remove'; + snapshot: database.DataSnapshot | null; +}; +type ResetAction = { type: 'reset' }; +type ValueAction = { type: 'value' }; +type ReducerAction = + | AddAction + | ChangeAction + | EmptyAction + | ErrorAction + | MoveAction + | RemoveAction + | ResetAction + | ValueAction; + +const initialState: ReducerState = { + loading: true, + value: { + keys: [], + values: [], + }, +}; + +const listReducer = ( + state: ReducerState, + action: ReducerAction +): ReducerState => { + switch (action.type) { + case 'add': + if (!action.snapshot) { + return state; + } + return { + ...state, + value: addChild(state.value, action.snapshot, action.previousKey), + }; + case 'change': + if (!action.snapshot) { + return state; + } + return { + ...state, + value: changeChild(state.value, action.snapshot), + }; + case 'error': + return { + ...state, + error: action.error, + loading: false, + }; + case 'move': + if (!action.snapshot) { + return state; + } + return { + ...state, + value: moveChild(state.value, action.snapshot, action.previousKey), + }; + case 'remove': + if (!action.snapshot) { + return state; + } + return { + ...state, + value: removeChild(state.value, action.snapshot), + }; + case 'reset': + return initialState; + case 'value': + return { + ...state, + loading: false, + }; + case 'empty': + return { + ...state, + loading: false, + value: { + keys: undefined, + values: undefined, + }, + }; + default: + return state; + } +}; + +const addChild = ( + currentState: KeyValueState, + snapshot: firebase.database.DataSnapshot, + previousKey?: string | null +): KeyValueState => { + if (!snapshot.key) { + return currentState; + } + + const { keys, values } = currentState; + if (!previousKey) { + // The child has been added to the start of the list + return { + keys: keys ? [snapshot.key, ...keys] : [snapshot.key], + values: values ? [snapshot, ...values] : [snapshot], + }; + } + // Establish the index for the previous child in the list + const index = keys ? keys.indexOf(previousKey) : 0; + // Insert the item after the previous child + return { + keys: keys + ? [...keys.slice(0, index + 1), snapshot.key, ...keys.slice(index + 1)] + : [snapshot.key], + values: values + ? [...values.slice(0, index + 1), snapshot, ...values.slice(index + 1)] + : [snapshot], + }; +}; + +const changeChild = ( + currentState: KeyValueState, + snapshot: firebase.database.DataSnapshot +): KeyValueState => { + if (!snapshot.key) { + return currentState; + } + const { keys, values } = currentState; + const index = keys ? keys.indexOf(snapshot.key) : 0; + return { + ...currentState, + values: values + ? [...values.slice(0, index), snapshot, ...values.slice(index + 1)] + : [snapshot], + }; +}; + +const removeChild = ( + currentState: KeyValueState, + snapshot: firebase.database.DataSnapshot +): KeyValueState => { + if (!snapshot.key) { + return currentState; + } + + const { keys, values } = currentState; + const index = keys ? keys.indexOf(snapshot.key) : 0; + return { + keys: keys ? [...keys.slice(0, index), ...keys.slice(index + 1)] : [], + values: values + ? [...values.slice(0, index), ...values.slice(index + 1)] + : [], + }; +}; + +const moveChild = ( + currentState: KeyValueState, + snapshot: firebase.database.DataSnapshot, + previousKey?: string | null +): KeyValueState => { + // Remove the child from it's previous location + const tempValue = removeChild(currentState, snapshot); + // Add the child into it's new location + return addChild(tempValue, snapshot, previousKey); +}; + +export default () => useReducer(listReducer, initialState); diff --git a/database/index.js.flow b/database/index.js.flow index 97aa80e..3eb6169 100644 --- a/database/index.js.flow +++ b/database/index.js.flow @@ -2,31 +2,13 @@ import typeof { FirebaseError } from 'firebase'; import type { DataSnapshot, Query } from 'firebase/database'; -export type ListHook = { - error?: FirebaseError, - loading: boolean, - value: DataSnapshot[], -}; -export type ListKeysHook = { - error?: FirebaseError, - loading: boolean, - value: string[], -}; -export type ListValsHook = { - error?: FirebaseError, - loading: boolean, - value: T[], -}; -export type ObjectHook = { - error?: FirebaseError, - loading: boolean, - value?: DataSnapshot, -}; -export type ObjectValHook = { - error?: FirebaseError, - loading: boolean, - value?: T, -}; +type LoadingHook = [T | void, boolean, FirebaseError | void]; + +export type ListHook = LoadingHook; +export type ListKeysHook = LoadingHook; +export type ListValsHook = LoadingHook; +export type ObjectHook = LoadingHook; +export type ObjectValHook = LoadingHook; declare export function useList(query?: Query | null): ListHook; declare export function useListKeys(query?: Query | null): ListKeysHook; diff --git a/database/index.ts b/database/index.ts index f17f5cd..6d45c07 100644 --- a/database/index.ts +++ b/database/index.ts @@ -1,5 +1,14 @@ -export { default as useList, ListHook } from './useList'; -export { default as useListKeys, ListKeysHook } from './useListKeys'; -export { default as useListVals, ListValsHook } from './useListVals'; -export { default as useObject, ObjectHook } from './useObject'; -export { default as useObjectVal, ObjectValHook } from './useObjectVal'; +export { + useList, + useListKeys, + useListVals, + ListHook, + ListKeysHook, + ListValsHook, +} from './useList'; +export { + useObject, + useObjectVal, + ObjectHook, + ObjectValHook, +} from './useObject'; diff --git a/database/useList.ts b/database/useList.ts index ddaf1b0..5fa2e79 100644 --- a/database/useList.ts +++ b/database/useList.ts @@ -1,127 +1,15 @@ import { database, FirebaseError } from 'firebase'; -import { useEffect, useReducer } from 'react'; -import { useIsEqualRef } from '../util'; +import { useEffect } from 'react'; +import { snapshotToData } from './helpers'; +import useListReducer from './helpers/useListReducer'; +import { LoadingHook, useIsEqualRef } from '../util'; -export type ListHook = { - error?: FirebaseError; - loading: boolean; - value?: database.DataSnapshot[]; -}; - -type KeyValueState = { - keys?: string[]; - values?: database.DataSnapshot[]; -}; - -type ReducerState = { - error?: FirebaseError; - loading: boolean; - value: KeyValueState; -}; - -type AddAction = { - type: 'add'; - previousKey?: string | null; - snapshot: database.DataSnapshot | null; -}; -type ChangeAction = { - type: 'change'; - snapshot: database.DataSnapshot | null; -}; -type EmptyAction = { type: 'empty' }; -type ErrorAction = { type: 'error'; error: FirebaseError }; -type MoveAction = { - type: 'move'; - previousKey?: string | null; - snapshot: database.DataSnapshot | null; -}; -type RemoveAction = { - type: 'remove'; - snapshot: database.DataSnapshot | null; -}; -type ResetAction = { type: 'reset' }; -type ValueAction = { type: 'value' }; -type ReducerAction = - | AddAction - | ChangeAction - | EmptyAction - | ErrorAction - | MoveAction - | RemoveAction - | ResetAction - | ValueAction; - -const initialState: ReducerState = { - loading: true, - value: { - keys: [], - values: [], - }, -}; - -const reducer = (state: ReducerState, action: ReducerAction): ReducerState => { - switch (action.type) { - case 'add': - if (!action.snapshot) { - return state; - } - return { - ...state, - value: addChild(state.value, action.snapshot, action.previousKey), - }; - case 'change': - if (!action.snapshot) { - return state; - } - return { - ...state, - value: changeChild(state.value, action.snapshot), - }; - case 'error': - return { - ...state, - error: action.error, - loading: false, - }; - case 'move': - if (!action.snapshot) { - return state; - } - return { - ...state, - value: moveChild(state.value, action.snapshot, action.previousKey), - }; - case 'remove': - if (!action.snapshot) { - return state; - } - return { - ...state, - value: removeChild(state.value, action.snapshot), - }; - case 'reset': - return initialState; - case 'value': - return { - ...state, - loading: false, - }; - case 'empty': - return { - ...state, - loading: false, - value: { - keys: undefined, - values: undefined, - }, - }; - default: - return state; - } -}; +export type ListHook = LoadingHook; +export type ListKeysHook = LoadingHook; +export type ListValsHook = LoadingHook; -export default (query?: database.Query | null): ListHook => { - const [state, dispatch] = useReducer(reducer, initialState); +export const useList = (query?: database.Query | null): ListHook => { + const [state, dispatch] = useListReducer(); const ref = useIsEqualRef(query, () => dispatch({ type: 'reset' })); @@ -179,85 +67,28 @@ export default (query?: database.Query | null): ListHook => { [ref.current] ); - return { - error: state.error, - loading: state.loading, - value: state.value.values, - }; -}; - -const addChild = ( - currentState: KeyValueState, - snapshot: firebase.database.DataSnapshot, - previousKey?: string | null -): KeyValueState => { - if (!snapshot.key) { - return currentState; - } - - const { keys, values } = currentState; - if (!previousKey) { - // The child has been added to the start of the list - return { - keys: keys ? [snapshot.key, ...keys] : [snapshot.key], - values: values ? [snapshot, ...values] : [snapshot], - }; - } - // Establish the index for the previous child in the list - const index = keys ? keys.indexOf(previousKey) : 0; - // Insert the item after the previous child - return { - keys: keys - ? [...keys.slice(0, index + 1), snapshot.key, ...keys.slice(index + 1)] - : [snapshot.key], - values: values - ? [...values.slice(0, index + 1), snapshot, ...values.slice(index + 1)] - : [snapshot], - }; + return [state.value.values, state.loading, state.error]; }; -const changeChild = ( - currentState: KeyValueState, - snapshot: firebase.database.DataSnapshot -): KeyValueState => { - if (!snapshot.key) { - return currentState; - } - const { keys, values } = currentState; - const index = keys ? keys.indexOf(snapshot.key) : 0; - return { - ...currentState, - values: values - ? [...values.slice(0, index), snapshot, ...values.slice(index + 1)] - : [snapshot], - }; -}; - -const removeChild = ( - currentState: KeyValueState, - snapshot: firebase.database.DataSnapshot -): KeyValueState => { - if (!snapshot.key) { - return currentState; - } - - const { keys, values } = currentState; - const index = keys ? keys.indexOf(snapshot.key) : 0; - return { - keys: keys ? [...keys.slice(0, index), ...keys.slice(index + 1)] : [], - values: values - ? [...values.slice(0, index), ...values.slice(index + 1)] - : [], - }; +export const useListKeys = (query?: database.Query | null): ListKeysHook => { + const [value, loading, error] = useList(query); + return [ + value ? value.map(snapshot => snapshot.key as string) : undefined, + loading, + error, + ]; }; -const moveChild = ( - currentState: KeyValueState, - snapshot: firebase.database.DataSnapshot, - previousKey?: string | null -): KeyValueState => { - // Remove the child from it's previous location - const tempValue = removeChild(currentState, snapshot); - // Add the child into it's new location - return addChild(tempValue, snapshot, previousKey); +export const useListVals = ( + query?: database.Query | null, + keyField?: string +): ListValsHook => { + const [value, loading, error] = useList(query); + return [ + value + ? value.map(snapshot => snapshotToData(snapshot, keyField)) + : undefined, + loading, + error, + ]; }; diff --git a/database/useListKeys.ts b/database/useListKeys.ts deleted file mode 100644 index 8f3f812..0000000 --- a/database/useListKeys.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { database, FirebaseError } from 'firebase'; -import useList from './useList'; - -export type ListKeysHook = { - error?: FirebaseError; - loading: boolean; - value?: string[]; -}; - -export default (query?: database.Query | null): ListKeysHook => { - const { error, loading, value } = useList(query); - return { - error, - loading, - value: value ? value.map(snapshot => snapshot.key as string) : undefined, - }; -}; diff --git a/database/useListVals.ts b/database/useListVals.ts deleted file mode 100644 index ed7ac35..0000000 --- a/database/useListVals.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { database, FirebaseError } from 'firebase'; -import useList from './useList'; -import { snapshotToData } from './helpers'; - -export type ListValsHook = { - error?: FirebaseError; - loading: boolean; - value?: T[]; -}; - -export default ( - query?: database.Query | null, - keyField?: string -): ListValsHook => { - const { error, loading, value } = useList(query); - return { - error, - loading, - value: value - ? value.map(snapshot => snapshotToData(snapshot, keyField)) - : undefined, - }; -}; diff --git a/database/useObject.ts b/database/useObject.ts index 5c7c4f3..82f1177 100644 --- a/database/useObject.ts +++ b/database/useObject.ts @@ -1,14 +1,12 @@ import { database, FirebaseError } from 'firebase'; import { useEffect } from 'react'; -import { useIsEqualRef, useLoadingValue } from '../util'; +import { snapshotToData } from './helpers'; +import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export type ObjectHook = { - error?: FirebaseError; - loading: boolean; - value?: database.DataSnapshot; -}; +export type ObjectHook = LoadingHook; +export type ObjectValHook = LoadingHook; -export default (query?: database.Query | null): ObjectHook => { +export const useObject = (query?: database.Query | null): ObjectHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< database.DataSnapshot >(); @@ -31,9 +29,13 @@ export default (query?: database.Query | null): ObjectHook => { [ref.current] ); - return { - error, - loading, - value, - }; + return [value, loading, error]; +}; + +export const useObjectVal = ( + query?: database.Query | null, + keyField?: string +): ObjectValHook => { + const [value, loading, error] = useObject(query); + return [value ? snapshotToData(value, keyField) : undefined, loading, error]; }; diff --git a/database/useObjectVal.ts b/database/useObjectVal.ts deleted file mode 100644 index ef46bad..0000000 --- a/database/useObjectVal.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { database, FirebaseError } from 'firebase'; -import useObject from './useObject'; -import { snapshotToData } from './helpers'; - -export type ObjectValHook = { - error?: FirebaseError; - loading: boolean; - value?: T; -}; - -export default ( - query?: database.Query | null, - keyField?: string -): ObjectValHook => { - const { error, loading, value } = useObject(query); - return { - error, - loading, - value: value ? snapshotToData(value, keyField) : undefined, - }; -}; diff --git a/firestore/README.md b/firestore/README.md index bef58fd..94516b6 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -12,39 +12,47 @@ There are 2 variants of each hook: - `useX` which subscribes to the underlying Collection or Document and listens for changes - `useXOnce` which reads the current value of the Collection or Document +All hooks can be imported from `react-firebase-hooks/firestore`, e.g. + +``` +import { useCollection } from 'react-firebase-hooks/firestore'; +``` + List of Cloud Firestore hooks: -- [useCollection](#usecollectionquery-options) -- [useCollectionOnce](#usecollectiononcequery-options) -- [useCollectionData](#usecollectiondatatref-idfield) -- [useCollectionDataOnce](#usecollectiondataoncetref-idfield) -- [useDocument](#usedocumentdocref) -- [useDocumentOnce](#usedocumentoncedocref) -- [useDocumentData](#usedocumentdatatref) -- [useDocumentDataOnce](#usedocumentdataoncetref) +- [useCollection](#usecollection) +- [useCollectionOnce](#usecollectiononce) +- [useCollectionData](#usecollectiondata) +- [useCollectionDataOnce](#usecollectiondataonce) +- [useDocument](#usedocument) +- [useDocumentOnce](#usedocumentonce) +- [useDocumentData](#usedocumentdata) +- [useDocumentDataOnce](#usedocumentdataonce) -### `useCollection(query, options)` +### useCollection -Parameters: +``` +const [snapshot, loading, error] = useCollection(query, snapshotListenOptions); +``` -- `query`: `firebase.firestore.Query` -- `options`: `firebase.firestore.SnapshotListenOptions` +Returns a `firebase.firestore.QuerySnapshot` (if a query is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. -Returns: -`CollectionHook` containing +The `useCollection` hook takes the following parameters: -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.QuerySnapshot` +- `query`: (optional) `firebase.firestore.Query` for the data you would like to load +- `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the query is loaded -#### Example +#### Full example ```js import { useCollection } from 'react-firebase-hooks/firestore'; const FirestoreCollection = () => { - const { error, loading, value } = useCollection( - firebase.firestore().collection('hooks') + const [value, loading, error] = useCollection( + firebase.firestore().collection('hooks'), + { + includeMetadataChanges: true, + } ); return (
@@ -67,95 +75,79 @@ const FirestoreCollection = () => { }; ``` -### `useCollectionOnce(query, options)` - -Parameters: +### useCollectionOnce -- `query`: `firebase.firestore.Query` -- `options`: `firebase.firestore.GetOptions` - -Returns: -`CollectionHook` containing +``` +const [snapshot, loading, error] = useCollectionOnce(query, getOptions); +``` -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.QuerySnapshot` +As `useCollection`, but this hook will only read the current value of the `firebase.firestore.Query`. -Import: +Returns a `firebase.firestore.QuerySnapshot` (if a query is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. -```js -import { useCollectionOnce } from 'react-firebase-hooks/firestore'; -``` +The `useCollectionOnce` hook takes the following parameters: -### `useCollectionData(ref, idField)` +- `query`: (optional) `firebase.firestore.Query` for the data you would like to load +- `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the collection is loaded -As `useCollection`, but this hook returns a typed list of the -`QuerySnapshot.docs` values, rather than the the `QuerySnapshot` itself. +### useCollectionData -Parameters: +``` +const [values, loading, error] = useCollectionData(query, options); +``` -- `query`: `firebase.firestore.Query` -- `options`: (Optional) Object containing: - - `snapshotListenOptions`: (Optional) `firebase.firestore.SnapshotListenOptions` - - `idField`: (Optional) Name of field that should be populated with the `QuerySnapshot.id` property +As `useCollection`, but this hook returns a typed list of the `firebase.firestore.QuerySnapshot.docs` values rather than the +`QuerySnapshot` itself. -Returns: -`CollectionDataHook` containing +The `useCollectionData` hook takes the following parameters: -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.firestore.DocumentSnapshot.data()` values, combined with the optional id field +- `query`: (optional) `firebase.firestore.Query` for the data you would like to load +- `options`: (optional) `Object` with the following parameters: + - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.id` property. + - `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the collection is loaded -Import: +### useCollectionDataOnce -```js -import { useCollectionData } from 'react-firebase-hooks/firestore'; +``` +const [value, loading, error] = useCollectionDataOnce(query, options); ``` -### `useCollectionDataOnce(ref, idField)` - -Parameters: +As `useCollectionData`, but this hook will only read the current value of the `firebase.firestore.Query`. -- `query`: `firebase.firestore.Query` -- `options`: (Optional) Object containing: - - `getOptions`: (Optional) `firebase.firestore.GetOptions` - - `idField`: (Optional) Name of field that should be populated with the `QuerySnapshot.id` property +Returns a typed list of the `firebase.firestore.QuerySnapshot.docs` values rather than the +`QuerySnapshot` itself. -Returns: -`CollectionDataHook` containing +The `useCollectionDataOnce` hook takes the following parameters: -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A list of `firebase.firestore.DocumentSnapshot.data()` values, combined with the optional id field +- `query`: (optional) `firebase.firestore.Query` for the data you would like to load +- `options`: (optional) `Object` with the following parameters: + - `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the collection is loaded + - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.id` property. -Import: +### useDocument -```js -import { useCollectionDataOnce } from 'react-firebase-hooks/firestore'; +``` +const [snapshot, loading, error] = useDocument(reference, snapshotListenOptions); ``` -### `useDocument(docRef)` - -Parameters: - -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) `firebase.firestore.SnapshotListenOptions` +Returns a `firebase.firestore.DocumentSnapshot` (if a document is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. -Returns: -`DocumentHook` containing +The `useDocument` hook takes the following parameters: -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.DocumentSnapshot` +- `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load +- `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the document is loaded -#### Example +#### Full example ```js import { useDocument } from 'react-firebase-hooks/firestore'; const FirestoreDocument = () => { - const { error, loading, value } = useDocument( - firebase.firestore().doc('hooks/nBShXiRGFAhuiPfBaGpt') + const [value, loading, error] = useDocument( + firebase.firestore().doc('hooks/nBShXiRGFAhuiPfBaGpt'), + { + includeMetadataChanges: true, + } ); return (
@@ -169,69 +161,50 @@ const FirestoreDocument = () => { }; ``` -### `useDocumentOnce(docRef)` - -Parameters: - -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) `firebase.firestore.GetOptions` +### useDocumentOnce -Returns: -`DocumentHook` containing - -- `error`: An optional `firebase.FirebaseError` returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: A `firebase.firestore.DocumentSnapshot` - -Import: - -```js -import { useDocumentOnce } from 'react-firebase-hooks/firestore'; +``` +const [snapshot, loading, error] = useDocumentOnce(reference, getOptions); ``` -### `useDocumentData(ref)` +As `useDocument`, but this hook will only read the current value of the `firebase.firestore.DocumentReference`. -As `useDocument`, but this hook returns the typed contents of -`DocumentSnapshot.val()` rather than the `DocumentSnapshot` itself. +Returns a `firebase.firestore.DocumentSnapshot` (if a reference is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. -Parameters: +The `useDocumentOnce` hook takes the following parameters: -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) Object containing: - - `snapshotListenOptions`: (Optional) `firebase.firestore.SnapshotListenOptions` - - `idField`: (Optional) Name of field that should be populated with the `DocumentSnapshot.id` property +- `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load +- `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the document is loaded -Returns: -`DocumentDataHook` containing +### useDocumentData -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: The contents of `firebase.firestore.DocumentSnapshot.data()`, combined with the optional id field +``` +const [values, loading, error] = useDocumentData(reference, options); +``` -Import: +As `useDocument`, but this hook returns the typed contents of `firebase.firestore.DocumentSnapshot.val()` values rather than the +`DocumentSnapshot` itself. -```js -import { useDocumentData } from 'react-firebase-hooks/firestore'; -``` +The `useDocumentData` hook takes the following parameters: -### `useDocumentDataOnce(ref)` +- `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load +- `options`: (optional) `Object` with the following parameters: + - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.DocumentSnapshot.id` property. + - `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the document is loaded -Parameters: +### useDocumentDataOnce -- `docRef`: `firebase.firestore.DocumentReference` -- `options`: (Optional) Object containing: - - `getOptions`: (Optional) `firebase.firestore.GetOptions` - - `idField`: (Optional) Name of field that should be populated with the `DocumentSnapshot.id` property +``` +const [value, loading, error] = useDocumentDataOnce(reference, options); +``` -Returns: -`DocumentDataHook` containing +As `useDocumentData`, but this hook will only read the current value of the `firebase.firestore.DocumentReference`. -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the listener is still being loaded -- `value`: The contents of `firebase.firestore.DocumentSnapshot.data()`, combined with the optional id field +Returns the typed contents of `firebase.firestore.DocumentSnapshot.data()` rather than the `DocumentSnapshot` itself. -Import: +The `useDocumentDataOnce` hook takes the following parameters: -```js -import { useDocumentDataOnce } from 'react-firebase-hooks/firestore'; -``` +- `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load +- `options`: (optional) `Object` with the following parameters: + - `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the document is loaded + - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.DocumentSnapshot.id` property. diff --git a/firestore/index.js.flow b/firestore/index.js.flow index 4abe47d..c93cd54 100644 --- a/firestore/index.js.flow +++ b/firestore/index.js.flow @@ -9,26 +9,12 @@ import type { SnapshotListenOptions, } from 'firebase/firestore'; -export type CollectionHook = { - error?: FirebaseError, - loading: boolean, - value?: QuerySnapshot, -}; -export type CollectionDataHook = { - error?: FirebaseError, - loading: boolean, - value?: T, -}; -export type DocumentHook = { - error?: FirebaseError, - loading: boolean, - value?: DocumentSnapshot, -}; -export type DocumentDataHook = { - error?: FirebaseError, - loading: boolean, - value?: T, -}; +type LoadingHook = [T | void, boolean, FirebaseError | void]; + +export type CollectionHook = LoadingHook; +export type CollectionDataHook = LoadingHook; +export type DocumentHook = LoadingHook; +export type DocumentDataHook = LoadingHook; declare export function useCollection( query?: Query | null, diff --git a/firestore/index.ts b/firestore/index.ts index b998fa7..d6c2e87 100644 --- a/firestore/index.ts +++ b/firestore/index.ts @@ -1,14 +1,24 @@ -export { default as useCollection, CollectionHook } from './useCollection'; -export { default as useCollectionOnce } from './useCollectionOnce'; export { - default as useCollectionData, + useCollection, + useCollectionData, + CollectionHook, CollectionDataHook, -} from './useCollectionData'; -export { default as useCollectionDataOnce } from './useCollectionDataOnce'; -export { default as useDocument, DocumentHook } from './useDocument'; -export { default as useDocumentOnce } from './useDocumentOnce'; +} from './useCollection'; export { - default as useDocumentData, + useCollectionOnce, + useCollectionDataOnce, + CollectionOnceHook, + CollectionDataOnceHook, +} from './useCollectionOnce'; +export { + useDocument, + useDocumentData, + DocumentHook, DocumentDataHook, -} from './useDocumentData'; -export { default as useDocumentDataOnce } from './useDocumentDataOnce'; +} from './useDocument'; +export { + useDocumentOnce, + useDocumentDataOnce, + DocumentOnceHook, + DocumentDataOnceHook, +} from './useDocumentOnce'; diff --git a/firestore/useCollection.ts b/firestore/useCollection.ts index 80c24cd..9fc78d4 100644 --- a/firestore/useCollection.ts +++ b/firestore/useCollection.ts @@ -1,15 +1,15 @@ import { firestore, FirebaseError } from 'firebase'; import { useEffect } from 'react'; -import { transformError } from './helpers'; -import { useIsEqualRef, useLoadingValue } from '../util'; +import { snapshotToData, transformError } from './helpers'; +import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export type CollectionHook = { - error?: FirebaseError; - loading: boolean; - value?: firestore.QuerySnapshot; -}; +export type CollectionHook = LoadingHook< + firestore.QuerySnapshot, + FirebaseError +>; +export type CollectionDataHook = LoadingHook; -export default ( +export const useCollection = ( query?: firestore.Query | null, options?: firestore.SnapshotListenOptions ): CollectionHook => { @@ -39,9 +39,26 @@ export default ( [ref.current] ); - return { - error, + return [value, loading, error]; +}; + +export const useCollectionData = ( + query?: firestore.Query | null, + options?: { + idField?: string; + snapshotListenOptions?: firestore.SnapshotListenOptions; + } +): CollectionDataHook => { + const idField = options ? options.idField : undefined; + const snapshotListenOptions = options + ? options.snapshotListenOptions + : undefined; + const [value, loading, error] = useCollection(query, snapshotListenOptions); + return [ + (value + ? value.docs.map(doc => snapshotToData(doc, idField)) + : undefined) as T[], loading, - value, - }; + error, + ]; }; diff --git a/firestore/useCollectionData.ts b/firestore/useCollectionData.ts deleted file mode 100644 index dedfe44..0000000 --- a/firestore/useCollectionData.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { firestore, FirebaseError } from 'firebase'; -import useCollection from './useCollection'; -import { snapshotToData } from './helpers'; - -export type CollectionDataHook = { - error?: FirebaseError; - loading: boolean; - value?: T[]; -}; - -export default ( - query?: firestore.Query | null, - options?: { - idField?: string; - snapshotListenOptions?: firestore.SnapshotListenOptions; - } -): CollectionDataHook => { - const idField = options ? options.idField : undefined; - const snapshotListenOptions = options - ? options.snapshotListenOptions - : undefined; - const { error, loading, value } = useCollection(query, snapshotListenOptions); - return { - error, - loading, - value: (value - ? value.docs.map(doc => snapshotToData(doc, idField)) - : undefined) as T[], - }; -}; diff --git a/firestore/useCollectionDataOnce.ts b/firestore/useCollectionDataOnce.ts deleted file mode 100644 index b72754e..0000000 --- a/firestore/useCollectionDataOnce.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { firestore } from 'firebase'; -import useCollectionOnce from './useCollectionOnce'; -import { snapshotToData } from './helpers'; -import { CollectionDataHook } from './useCollectionData'; - -export default ( - query?: firestore.Query | null, - options?: { - getOptions?: firestore.GetOptions; - idField?: string; - } -): CollectionDataHook => { - const idField = options ? options.idField : undefined; - const getOptions = options ? options.getOptions : undefined; - const { error, loading, value } = useCollectionOnce(query, getOptions); - return { - error, - loading, - value: (value - ? value.docs.map(doc => snapshotToData(doc, idField)) - : undefined) as T[], - }; -}; diff --git a/firestore/useCollectionOnce.ts b/firestore/useCollectionOnce.ts index 95646c7..ac71fb8 100644 --- a/firestore/useCollectionOnce.ts +++ b/firestore/useCollectionOnce.ts @@ -1,12 +1,18 @@ -import { firestore } from 'firebase'; +import { firestore, FirebaseError } from 'firebase'; import { useEffect } from 'react'; -import { useIsEqualRef, useLoadingValue } from '../util'; -import { CollectionHook } from './useCollection'; +import { snapshotToData } from './helpers'; +import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export default ( +export type CollectionOnceHook = LoadingHook< + firestore.QuerySnapshot, + FirebaseError +>; +export type CollectionDataOnceHook = LoadingHook; + +export const useCollectionOnce = ( query?: firestore.Query | null, options?: firestore.GetOptions -): CollectionHook => { +): CollectionOnceHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< firestore.QuerySnapshot >(); @@ -26,9 +32,24 @@ export default ( [ref.current] ); - return { - error, + return [value, loading, error]; +}; + +export const useCollectionDataOnce = ( + query?: firestore.Query | null, + options?: { + getOptions?: firestore.GetOptions; + idField?: string; + } +): CollectionDataOnceHook => { + const idField = options ? options.idField : undefined; + const getOptions = options ? options.getOptions : undefined; + const [value, loading, error] = useCollectionOnce(query, getOptions); + return [ + (value + ? value.docs.map(doc => snapshotToData(doc, idField)) + : undefined) as T[], loading, - value, - }; + error, + ]; }; diff --git a/firestore/useDocument.ts b/firestore/useDocument.ts index a5c21c8..e01c47c 100644 --- a/firestore/useDocument.ts +++ b/firestore/useDocument.ts @@ -1,15 +1,15 @@ import { firestore, FirebaseError } from 'firebase'; import { useEffect } from 'react'; -import { transformError } from './helpers'; -import { useIsEqualRef, useLoadingValue } from '../util'; +import { snapshotToData, transformError } from './helpers'; +import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export type DocumentHook = { - error?: FirebaseError; - loading: boolean; - value?: firestore.DocumentSnapshot; -}; +export type DocumentHook = LoadingHook< + firestore.DocumentSnapshot, + FirebaseError +>; +export type DocumentDataHook = LoadingHook; -export default ( +export const useDocument = ( docRef?: firestore.DocumentReference | null, options?: firestore.SnapshotListenOptions ): DocumentHook => { @@ -39,9 +39,24 @@ export default ( [ref.current] ); - return { - error, + return [value, loading, error]; +}; + +export const useDocumentData = ( + docRef?: firestore.DocumentReference | null, + options?: { + idField?: string; + snapshotListenOptions?: firestore.SnapshotListenOptions; + } +): DocumentDataHook => { + const idField = options ? options.idField : undefined; + const snapshotListenOptions = options + ? options.snapshotListenOptions + : undefined; + const [value, loading, error] = useDocument(docRef, snapshotListenOptions); + return [ + (value ? snapshotToData(value, idField) : undefined) as T, loading, - value, - }; + error, + ]; }; diff --git a/firestore/useDocumentData.ts b/firestore/useDocumentData.ts deleted file mode 100644 index 3661cb2..0000000 --- a/firestore/useDocumentData.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { firestore, FirebaseError } from 'firebase'; -import useDocument from './useDocument'; -import { snapshotToData } from './helpers'; - -export type DocumentDataHook = { - error?: FirebaseError; - loading: boolean; - value?: T; -}; - -export default ( - docRef?: firestore.DocumentReference | null, - options?: { - idField?: string; - snapshotListenOptions?: firestore.SnapshotListenOptions; - } -): DocumentDataHook => { - const idField = options ? options.idField : undefined; - const snapshotListenOptions = options - ? options.snapshotListenOptions - : undefined; - const { error, loading, value } = useDocument(docRef, snapshotListenOptions); - return { - error, - loading, - value: (value ? snapshotToData(value, idField) : undefined) as T, - }; -}; diff --git a/firestore/useDocumentDataOnce.ts b/firestore/useDocumentDataOnce.ts deleted file mode 100644 index 0f47bd6..0000000 --- a/firestore/useDocumentDataOnce.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { firestore } from 'firebase'; -import useDocumentOnce from './useDocumentOnce'; -import { snapshotToData } from './helpers'; -import { DocumentDataHook } from './useDocumentData'; - -export default ( - docRef?: firestore.DocumentReference | null, - options?: { - getOptions?: firestore.GetOptions; - idField?: string; - } -): DocumentDataHook => { - const idField = options ? options.idField : undefined; - const getOptions = options ? options.getOptions : undefined; - const { error, loading, value } = useDocumentOnce(docRef, getOptions); - return { - error, - loading, - value: (value ? snapshotToData(value, idField) : undefined) as T, - }; -}; diff --git a/firestore/useDocumentOnce.ts b/firestore/useDocumentOnce.ts index 8b8f7a2..eb0c31e 100644 --- a/firestore/useDocumentOnce.ts +++ b/firestore/useDocumentOnce.ts @@ -1,12 +1,18 @@ -import { firestore } from 'firebase'; +import { firestore, FirebaseError } from 'firebase'; import { useEffect } from 'react'; -import { useIsEqualRef, useLoadingValue } from '../util'; -import { DocumentHook } from './useDocument'; +import { snapshotToData } from './helpers'; +import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export default ( +export type DocumentOnceHook = LoadingHook< + firestore.DocumentSnapshot, + FirebaseError +>; +export type DocumentDataOnceHook = LoadingHook; + +export const useDocumentOnce = ( docRef?: firestore.DocumentReference | null, options?: firestore.GetOptions -): DocumentHook => { +): DocumentOnceHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< firestore.DocumentSnapshot >(); @@ -26,9 +32,22 @@ export default ( [ref.current] ); - return { - error, + return [value, loading, error]; +}; + +export const useDocumentDataOnce = ( + docRef?: firestore.DocumentReference | null, + options?: { + getOptions?: firestore.GetOptions; + idField?: string; + } +): DocumentDataOnceHook => { + const idField = options ? options.idField : undefined; + const getOptions = options ? options.getOptions : undefined; + const [value, loading, error] = useDocumentOnce(docRef, getOptions); + return [ + (value ? snapshotToData(value, idField) : undefined) as T, loading, - value, - }; + error, + ]; }; diff --git a/package.json b/package.json index 9be2257..74f86cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "1.2.1", + "version": "2.0.0-alpha.2", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", diff --git a/storage/README.md b/storage/README.md index 69e75e8..2acaecb 100644 --- a/storage/README.md +++ b/storage/README.md @@ -6,30 +6,35 @@ Firebase Cloud Storage. The hooks wrap around the `firebase.storage().ref().getD In addition to returning the download URL, the hooks provide an `error` and `loading` property to give a complete lifecycle for loading from Cloud Storage. +All hooks can be imported from `react-firebase-hooks/storage`, e.g. + +``` +import { useCollection } from 'react-firebase-hooks/storage'; +``` + List of Cloud Storage hooks: -- [useDownloadURL](#usedownloadurlref) +- [useDownloadURL](#usedownloadurl) -### `useDownloadURL(ref)` +### useDownloadURL -Parameters: +``` +const [downloadUrl, loading, error] = useDownloadURL(reference); +``` -- `ref`: `firebase.storage.Reference` +Returns the download URL as a `string` (if a reference is supplied), a boolean to indicate whether the the download URL is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the download URL. -Returns: -`DownloadURLHook` containing +The `useDownloadURL` hook takes the following parameters: -- `error`: An optional error object returned by Firebase -- `loading`: A `boolean` to indicate if the download URL is still being loaded -- `value`: The download URL +- `reference`: (optional) `firebase.storage.Reference` that you would like the download URL for -#### Example +#### Full example ```js import { useDownloadURL } from 'react-firebase-hooks/storage'; const DownloadURL = () => { - const { error, loading, value } = useDownloadURL( + const [value, loading, error] = useDownloadURL( firebase.storage().ref('path/to/file') ); diff --git a/storage/index.js.flow b/storage/index.js.flow index a54f38a..946d495 100644 --- a/storage/index.js.flow +++ b/storage/index.js.flow @@ -2,9 +2,7 @@ import typeof { FirebaseError } from 'firebase'; import type { Reference } from 'firebase/storage'; -export type DownloadURLHook = { - error?: FirebaseError, - loading: boolean, - value?: string, -}; +type LoadingHook = [T | void, boolean, FirebaseError | void]; + +export type DownloadURLHook = LoadingHook; declare export function useDownloadURL(ref?: Reference | null): DownloadURLHook; diff --git a/storage/useDownloadURL.ts b/storage/useDownloadURL.ts index 85cc1f2..b0e7eb3 100644 --- a/storage/useDownloadURL.ts +++ b/storage/useDownloadURL.ts @@ -1,12 +1,8 @@ import { storage, FirebaseError } from 'firebase'; import { useEffect } from 'react'; -import { useComparatorRef, useLoadingValue } from '../util'; +import { LoadingHook, useComparatorRef, useLoadingValue } from '../util'; -export type DownloadURLHook = { - error?: FirebaseError; - loading: boolean; - value?: string; -}; +export type DownloadURLHook = LoadingHook; export default (storageRef?: storage.Reference | null): DownloadURLHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< @@ -28,11 +24,7 @@ export default (storageRef?: storage.Reference | null): DownloadURLHook => { [ref.current] ); - return { - error, - loading, - value, - }; + return [value, loading, error]; }; const isEqual = ( diff --git a/util/index.ts b/util/index.ts index 9adf37f..ce8a13c 100644 --- a/util/index.ts +++ b/util/index.ts @@ -1,2 +1,4 @@ export { default as useLoadingValue } from './useLoadingValue'; export * from './refHooks'; + +export type LoadingHook = [T | void, boolean, E | void]; From 67dea13797943935ea11b7f1b9f202f3ef5afef1 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Thu, 16 May 2019 15:28:29 +0100 Subject: [PATCH 08/12] Tweak error responses and API structure --- auth/README.md | 8 ++- auth/helpers/index.ts | 10 ---- auth/index.js.flow | 4 +- auth/useAuthState.ts | 16 +++-- database/README.md | 56 ++++++++++++++---- database/index.js.flow | 11 +++- database/useList.ts | 8 ++- database/useObject.ts | 15 ++++- firestore/README.md | 105 +++++++++++++++++++++++---------- firestore/helpers/index.ts | 11 +--- firestore/index.js.flow | 19 ++++-- firestore/useCollection.ts | 37 ++++++------ firestore/useCollectionOnce.ts | 20 +++---- firestore/useDocument.ts | 37 ++++++------ firestore/useDocumentOnce.ts | 20 +++---- package.json | 2 +- storage/README.md | 8 ++- storage/useDownloadURL.ts | 3 +- util/useLoadingValue.ts | 29 +++++---- 19 files changed, 263 insertions(+), 156 deletions(-) delete mode 100644 auth/helpers/index.ts diff --git a/auth/README.md b/auth/README.md index 7e15067..6210306 100644 --- a/auth/README.md +++ b/auth/README.md @@ -18,12 +18,18 @@ List of Auth hooks: const [user, loading, error] = useAuthState(auth); ``` -Returns the `firebase.User` (if logged in), a boolean to indicate whether the the user is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the user. +Retrieve and monitor the authentication state from Firebase. The `useAuthState` hook takes the following parameters: - `auth`: `firebase.auth.Auth` instance for the app you would like to monitor +Returns: + +- `user`: The `firebase.User` if logged in, or `void` if not +- `loading`: A `boolean` to indicate whether the the authentication state is still being loaded +- `error`: Any `firebase.auth.Error` returned by Firebase when trying to load the user, or `void` if there is no error + #### Full Example ```js diff --git a/auth/helpers/index.ts b/auth/helpers/index.ts deleted file mode 100644 index 2795804..0000000 --- a/auth/helpers/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { auth, FirebaseError } from 'firebase'; - -export const transformError = (error: auth.Error): FirebaseError => { - return { - message: error.message, - stack: '', - name: '', - code: error.code, - }; -}; diff --git a/auth/index.js.flow b/auth/index.js.flow index 16476a3..560af41 100644 --- a/auth/index.js.flow +++ b/auth/index.js.flow @@ -1,9 +1,9 @@ // @flow import type { FirebaseUser as User } from 'firebase'; -import typeof { FirebaseError } from 'firebase'; import type { Auth } from 'firebase/auth'; +import typeof { Error as AuthError } from 'firebase/auth'; -type LoadingHook = [T | void, boolean, FirebaseError | void]; +type LoadingHook = [T | void, boolean, AuthError | void]; export type AuthStateHook = LoadingHook; declare export function useAuthState(auth: Auth): AuthStateHook; diff --git a/auth/useAuthState.ts b/auth/useAuthState.ts index 9969c0d..e5606ad 100644 --- a/auth/useAuthState.ts +++ b/auth/useAuthState.ts @@ -1,20 +1,18 @@ -import { auth, FirebaseError, User } from 'firebase'; +import { auth, User } from 'firebase'; import { useEffect } from 'react'; -import { transformError } from './helpers'; import { LoadingHook, useLoadingValue } from '../util'; -export type AuthStateHook = LoadingHook; +export type AuthStateHook = LoadingHook; export default (auth: auth.Auth): AuthStateHook => { - const { error, loading, setError, setValue, value } = useLoadingValue( - () => auth.currentUser - ); + const { error, loading, setError, setValue, value } = useLoadingValue< + User, + auth.Error + >(() => auth.currentUser); useEffect( () => { - const listener = auth.onAuthStateChanged(setValue, (error: auth.Error) => - setError(transformError(error)) - ); + const listener = auth.onAuthStateChanged(setValue, setError); return () => { listener(); diff --git a/database/README.md b/database/README.md index 9329272..e51a84c 100644 --- a/database/README.md +++ b/database/README.md @@ -15,7 +15,7 @@ import { useList } from 'react-firebase-hooks/database'; List of Realtime Database hooks: - [useList](#uselistref) -- [useListKeys](#uselistkeyst) +- [useListKeys](#uselistkeys) - [useListVals](#uselistvals) - [useObject](#useobjectref) - [useObjectVal](#useobjectval) @@ -26,12 +26,18 @@ List of Realtime Database hooks: const [snapshots, loading, error] = useList(reference); ``` -Returns an array of `firebase.database.DataSnapshot` (if a reference is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. +Retrieve and monitor a list value in the Firebase Realtime Database. The `useList` hook takes the following parameters: - `reference`: (optional) `firebase.database.Reference` for the data you would like to load +Returns: + +- `snapshots`: an array of `firebase.database.DataSnapshot`, or `void` if no reference is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `firebase.FirebaseError` returned by Firebase when trying to load the data, or `void` if there is no error + #### Full Example ```js @@ -67,25 +73,38 @@ const DatabaseList = () => { const [keys, loading, error] = useListKeys(reference); ``` -As `useList`, but this hook returns a list of the `firebase.database.DataSnapshot.key` values, rather than the the `firebase.database.DataSnapshot`s themselves. +As `useList`, but this hooks extracts the `firebase.database.DataSnapshot.key` values, rather than the the `firebase.database.DataSnapshot`s themselves. The `useListKeys` hook takes the following parameters: - `reference`: (optional) `firebase.database.Reference` for the data you would like to load +Returns: + +- `keys`: an array of `string`, or `void` if no reference is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `firebase.FirebaseError` returned by Firebase when trying to load the data, or `void` if there is no error + ### useListVals ``` -const [values, loading, error] = useListVals(reference, keyField); +const [values, loading, error] = useListVals(reference, options); ``` -As `useList`, but this hook returns a typed list of the `firebase.database.DataSnapshot.val()` values, rather than the the -`DataSnapshot`s themselves. +As `useList`, but this hook extracts a typed list of the `firebase.database.DataSnapshot.val()` values, rather than the the +`firebase.database.DataSnapshot`s themselves. The `useListVals` hook takes the following parameters: - `reference`: (optional) `firebase.database.Reference` for the data you would like to load -- `keyField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.key` property in the returned value. +- `options`: (optional) `Object` with the following parameters: + - `keyField`: (optional) `string` field name that should be populated with the `firebase.firestore.QuerySnapshot.id` property in the returned values + +Returns: + +- `values`: an array of `T`, or `void` if no reference is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `firebase.FirebaseError` returned by Firebase when trying to load the data, or `void` if there is no error ### useObject @@ -93,12 +112,18 @@ The `useListVals` hook takes the following parameters: const [snapshot, loading, error] = useObject(reference); ``` -Returns a `firebase.database.DataSnapshot` (if a reference is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. +Retrieve and monitor an object or primitive value in the Firebase Realtime Database. The `useObject` hook takes the following parameters: - `reference`: (optional) `firebase.database.Reference` for the data you would like to load +Returns: + +- `snapshot`: a `firebase.database.DataSnapshot`, or `void` if no reference is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `firebase.FirebaseError` returned by Firebase when trying to load the data, or `void` if there is no error + #### Full Example ```js @@ -122,13 +147,20 @@ const DatabaseValue = () => { ### useObjectVal ``` -const [value, loading, error] = useObjectVal(reference, keyField); +const [value, loading, error] = useObjectVal(reference, options); ``` -As `useObject`, but this hook returns the typed contents of `DataSnapshot.val()` rather than the -`DataSnapshot` itself. +As `useObject`, but this hook returns the typed contents of `firebase.database.DataSnapshot.val()`, rather than the the +`firebase.database.DataSnapshot` itself. The `useObjectVal` hook takes the following parameters: - `reference`: (optional) `firebase.database.Reference` for the data you would like to load -- `keyField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.key` property in the returned value. +- `options`: (optional) `Object` with the following parameters: + - `keyField`: (optional) `string` field name that should be populated with the `firebase.database.DataSnapshot.key` property in the returned value. + +Returns: + +- `value`: a `T`, or `void` if no reference is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `firebase.FirebaseError` returned by Firebase when trying to load the data, or `void` if there is no error diff --git a/database/index.js.flow b/database/index.js.flow index 3eb6169..0a4201d 100644 --- a/database/index.js.flow +++ b/database/index.js.flow @@ -14,7 +14,14 @@ declare export function useList(query?: Query | null): ListHook; declare export function useListKeys(query?: Query | null): ListKeysHook; declare export function useListVals( query?: Query | null, - keyField?: string + options?: { + keyField?: string, + } ): ListValsHook; declare export function useObject(query?: Query | null): ObjectHook; -declare export function useObjectVal(query?: Query | null): ObjectValHook; +declare export function useObjectVal( + query?: Query | null, + options?: { + keyField?: string, + } +): ObjectValHook; diff --git a/database/useList.ts b/database/useList.ts index 5fa2e79..aeed4a4 100644 --- a/database/useList.ts +++ b/database/useList.ts @@ -81,12 +81,16 @@ export const useListKeys = (query?: database.Query | null): ListKeysHook => { export const useListVals = ( query?: database.Query | null, - keyField?: string + options?: { + keyField?: string; + } ): ListValsHook => { const [value, loading, error] = useList(query); return [ value - ? value.map(snapshot => snapshotToData(snapshot, keyField)) + ? value.map(snapshot => + snapshotToData(snapshot, options ? options.keyField : undefined) + ) : undefined, loading, error, diff --git a/database/useObject.ts b/database/useObject.ts index 82f1177..77eb650 100644 --- a/database/useObject.ts +++ b/database/useObject.ts @@ -8,7 +8,8 @@ export type ObjectValHook = LoadingHook; export const useObject = (query?: database.Query | null): ObjectHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< - database.DataSnapshot + database.DataSnapshot, + FirebaseError >(); const ref = useIsEqualRef(query, reset); @@ -34,8 +35,16 @@ export const useObject = (query?: database.Query | null): ObjectHook => { export const useObjectVal = ( query?: database.Query | null, - keyField?: string + options?: { + keyField?: string; + } ): ObjectValHook => { const [value, loading, error] = useObject(query); - return [value ? snapshotToData(value, keyField) : undefined, loading, error]; + return [ + value + ? snapshotToData(value, options ? options.keyField : undefined) + : undefined, + loading, + error, + ]; }; diff --git a/firestore/README.md b/firestore/README.md index 94516b6..cd0f526 100644 --- a/firestore/README.md +++ b/firestore/README.md @@ -32,15 +32,24 @@ List of Cloud Firestore hooks: ### useCollection ``` -const [snapshot, loading, error] = useCollection(query, snapshotListenOptions); +const [snapshot, loading, error] = useCollection(query, options); ``` -Returns a `firebase.firestore.QuerySnapshot` (if a query is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. +Retrieve and monitor a collection value in Cloud Firestore. + +Returns a `firebase.firestore.QuerySnapshot` (if a query is specified), a `boolean` to indicate if the data is still being loaded and any `Error` returned by Firebase when trying to load the data. The `useCollection` hook takes the following parameters: - `query`: (optional) `firebase.firestore.Query` for the data you would like to load -- `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the query is loaded +- `options`: (optional) `Object` with the following parameters: + - `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the query is loaded + +Returns: + +- `snapshot`: a `firebase.firestore.QuerySnapshot`, or `void` if no query is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error #### Full example @@ -78,17 +87,22 @@ const FirestoreCollection = () => { ### useCollectionOnce ``` -const [snapshot, loading, error] = useCollectionOnce(query, getOptions); +const [snapshot, loading, error] = useCollectionOnce(query, options); ``` -As `useCollection`, but this hook will only read the current value of the `firebase.firestore.Query`. - -Returns a `firebase.firestore.QuerySnapshot` (if a query is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. +Retrieve the current value of the `firebase.firestore.Query`. The `useCollectionOnce` hook takes the following parameters: - `query`: (optional) `firebase.firestore.Query` for the data you would like to load -- `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the collection is loaded +- `options`: (optional) `Object` with the following parameters: + - `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the collection is loaded + +Returns: + +- `snapshot`: a `firebase.firestore.QuerySnapshot`, or `void` if no query is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error ### useCollectionData @@ -96,8 +110,8 @@ The `useCollectionOnce` hook takes the following parameters: const [values, loading, error] = useCollectionData(query, options); ``` -As `useCollection`, but this hook returns a typed list of the `firebase.firestore.QuerySnapshot.docs` values rather than the -`QuerySnapshot` itself. +As `useCollection`, but this hook extracts a typed list of the `firebase.firestore.QuerySnapshot.docs` values, rather than the +`firebase.firestore.QuerySnapshot` itself. The `useCollectionData` hook takes the following parameters: @@ -106,17 +120,20 @@ The `useCollectionData` hook takes the following parameters: - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.id` property. - `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the collection is loaded +Returns: + +- `values`: an array of `T`, or `void` if no query is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error + ### useCollectionDataOnce ``` -const [value, loading, error] = useCollectionDataOnce(query, options); +const [values, loading, error] = useCollectionDataOnce(query, options); ``` As `useCollectionData`, but this hook will only read the current value of the `firebase.firestore.Query`. -Returns a typed list of the `firebase.firestore.QuerySnapshot.docs` values rather than the -`QuerySnapshot` itself. - The `useCollectionDataOnce` hook takes the following parameters: - `query`: (optional) `firebase.firestore.Query` for the data you would like to load @@ -124,18 +141,31 @@ The `useCollectionDataOnce` hook takes the following parameters: - `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the collection is loaded - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.QuerySnapshot.id` property. +Returns: + +- `values`: an array of `T`, or `void` if no query is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error + ### useDocument ``` -const [snapshot, loading, error] = useDocument(reference, snapshotListenOptions); +const [snapshot, loading, error] = useDocument(reference, options); ``` -Returns a `firebase.firestore.DocumentSnapshot` (if a document is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. +Retrieve and monitor a document value in Cloud Firestore. The `useDocument` hook takes the following parameters: - `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load -- `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the document is loaded +- `options`: (optional) `Object` with the following parameters: + - `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the query is loaded + +Returns: + +- `snapshot`: a `firebase.firestore.DocumentSnapshot`, or `void` if no query is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error #### Full example @@ -164,33 +194,44 @@ const FirestoreDocument = () => { ### useDocumentOnce ``` -const [snapshot, loading, error] = useDocumentOnce(reference, getOptions); +const [snapshot, loading, error] = useDocumentOnce(reference, options); ``` -As `useDocument`, but this hook will only read the current value of the `firebase.firestore.DocumentReference`. - -Returns a `firebase.firestore.DocumentSnapshot` (if a reference is specified), a `boolean` to indicate if the data is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the data. +Retrieve the current value of the `firebase.firestore.DocumentReference`. The `useDocumentOnce` hook takes the following parameters: - `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load -- `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the document is loaded +- `options`: (optional) `Object` with the following parameters: + - `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the collection is loaded + +Returns: + +- `snapshot`: a `firebase.firestore.DocumentSnapshot`, or `void` if no reference is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error ### useDocumentData ``` -const [values, loading, error] = useDocumentData(reference, options); +const [value, loading, error] = useDocumentData(reference, options); ``` -As `useDocument`, but this hook returns the typed contents of `firebase.firestore.DocumentSnapshot.val()` values rather than the -`DocumentSnapshot` itself. +As `useDocument`, but this hook extracts the typed contents of `firebase.firestore.DocumentSnapshot.val()`, rather than the +`firebase.firestore.DocumentSnapshot` itself. The `useDocumentData` hook takes the following parameters: - `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load - `options`: (optional) `Object` with the following parameters: - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.DocumentSnapshot.id` property. - - `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the document is loaded + - `snapshotListenOptions`: (optional) `firebase.firestore.SnapshotListenOptions` to customise how the collection is loaded + +Returns: + +- `value`: `T`, or `void` if no query is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error ### useDocumentDataOnce @@ -198,13 +239,17 @@ The `useDocumentData` hook takes the following parameters: const [value, loading, error] = useDocumentDataOnce(reference, options); ``` -As `useDocumentData`, but this hook will only read the current value of the `firebase.firestore.DocumentReference`. - -Returns the typed contents of `firebase.firestore.DocumentSnapshot.data()` rather than the `DocumentSnapshot` itself. +As `useDocument`, but this hook will only read the current value of the `firebase.firestore.DocumentReference`. The `useDocumentDataOnce` hook takes the following parameters: - `reference`: (optional) `firebase.firestore.DocumentReference` for the data you would like to load - `options`: (optional) `Object` with the following parameters: - - `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the document is loaded + - `getOptions`: (optional) `firebase.firestore.GetOptions` to customise how the collection is loaded - `idField`: (optional) name of the field that should be populated with the `firebase.firestore.DocumentSnapshot.id` property. + +Returns: + +- `value`: `T`, or `void` if no query is supplied +- `loading`: a `boolean` to indicate if the data is still being loaded +- `error`: Any `Error` returned by Firebase when trying to load the data, or `void` if there is no error diff --git a/firestore/helpers/index.ts b/firestore/helpers/index.ts index 35082ae..eb5033a 100644 --- a/firestore/helpers/index.ts +++ b/firestore/helpers/index.ts @@ -1,4 +1,4 @@ -import { firestore, FirebaseError } from 'firebase'; +import { firestore } from 'firebase'; export const snapshotToData = ( snapshot: firestore.DocumentSnapshot, @@ -13,12 +13,3 @@ export const snapshotToData = ( ...(idField ? { [idField]: snapshot.id } : null), }; }; - -export const transformError = (error: Error): FirebaseError => { - return { - message: error.message, - stack: error.stack, - name: error.name, - code: '', - }; -}; diff --git a/firestore/index.js.flow b/firestore/index.js.flow index c93cd54..1fc8d8f 100644 --- a/firestore/index.js.flow +++ b/firestore/index.js.flow @@ -1,5 +1,4 @@ // @flow -import typeof { FirebaseError } from 'firebase'; import type { DocumentReference, DocumentSnapshot, @@ -9,7 +8,7 @@ import type { SnapshotListenOptions, } from 'firebase/firestore'; -type LoadingHook = [T | void, boolean, FirebaseError | void]; +type LoadingHook = [T | void, boolean, Error | void]; export type CollectionHook = LoadingHook; export type CollectionDataHook = LoadingHook; @@ -18,11 +17,15 @@ export type DocumentDataHook = LoadingHook; declare export function useCollection( query?: Query | null, - options?: SnapshotListenOptions + options?: { + snapshotListenOptions?: SnapshotListenOptions, + } ): CollectionHook; declare export function useCollectionOnce( query?: Query | null, - options?: GetOptions + options?: { + getOptions?: GetOptions, + } ): CollectionHook; declare export function useCollectionData( query?: Query | null, @@ -40,11 +43,15 @@ declare export function useCollectionDataOnce( ): CollectionDataHook; declare export function useDocument( ref?: DocumentReference | null, - options?: SnapshotListenOptions + options?: { + snapshotListenOptions?: SnapshotListenOptions, + } ): DocumentHook; declare export function useDocumentOnce( ref?: DocumentReference | null, - options?: GetOptions + options?: { + getOptions?: GetOptions, + } ): DocumentHook; declare export function useDocumentData( ref?: DocumentReference | null, diff --git a/firestore/useCollection.ts b/firestore/useCollection.ts index 9fc78d4..15a7dd8 100644 --- a/firestore/useCollection.ts +++ b/firestore/useCollection.ts @@ -1,20 +1,20 @@ -import { firestore, FirebaseError } from 'firebase'; +import { firestore } from 'firebase'; import { useEffect } from 'react'; -import { snapshotToData, transformError } from './helpers'; +import { snapshotToData } from './helpers'; import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export type CollectionHook = LoadingHook< - firestore.QuerySnapshot, - FirebaseError ->; -export type CollectionDataHook = LoadingHook; +export type CollectionHook = LoadingHook; +export type CollectionDataHook = LoadingHook; export const useCollection = ( query?: firestore.Query | null, - options?: firestore.SnapshotListenOptions + options?: { + snapshotListenOptions?: firestore.SnapshotListenOptions; + } ): CollectionHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< - firestore.QuerySnapshot + firestore.QuerySnapshot, + Error >(); const ref = useIsEqualRef(query, reset); @@ -24,13 +24,14 @@ export const useCollection = ( setValue(undefined); return; } - const listener = options - ? ref.current.onSnapshot(options, setValue, (error: Error) => - setError(transformError(error)) - ) - : ref.current.onSnapshot(setValue, (error: Error) => - setError(transformError(error)) - ); + const listener = + options && options.snapshotListenOptions + ? ref.current.onSnapshot( + options.snapshotListenOptions, + setValue, + setError + ) + : ref.current.onSnapshot(setValue, setError); return () => { listener(); @@ -53,7 +54,9 @@ export const useCollectionData = ( const snapshotListenOptions = options ? options.snapshotListenOptions : undefined; - const [value, loading, error] = useCollection(query, snapshotListenOptions); + const [value, loading, error] = useCollection(query, { + snapshotListenOptions, + }); return [ (value ? value.docs.map(doc => snapshotToData(doc, idField)) diff --git a/firestore/useCollectionOnce.ts b/firestore/useCollectionOnce.ts index ac71fb8..916c0a9 100644 --- a/firestore/useCollectionOnce.ts +++ b/firestore/useCollectionOnce.ts @@ -1,20 +1,20 @@ -import { firestore, FirebaseError } from 'firebase'; +import { firestore } from 'firebase'; import { useEffect } from 'react'; import { snapshotToData } from './helpers'; import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export type CollectionOnceHook = LoadingHook< - firestore.QuerySnapshot, - FirebaseError ->; -export type CollectionDataOnceHook = LoadingHook; +export type CollectionOnceHook = LoadingHook; +export type CollectionDataOnceHook = LoadingHook; export const useCollectionOnce = ( query?: firestore.Query | null, - options?: firestore.GetOptions + options?: { + getOptions?: firestore.GetOptions; + } ): CollectionOnceHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< - firestore.QuerySnapshot + firestore.QuerySnapshot, + Error >(); const ref = useIsEqualRef(query, reset); @@ -25,7 +25,7 @@ export const useCollectionOnce = ( return; } ref.current - .get(options) + .get(options ? options.getOptions : undefined) .then(setValue) .catch(setError); }, @@ -44,7 +44,7 @@ export const useCollectionDataOnce = ( ): CollectionDataOnceHook => { const idField = options ? options.idField : undefined; const getOptions = options ? options.getOptions : undefined; - const [value, loading, error] = useCollectionOnce(query, getOptions); + const [value, loading, error] = useCollectionOnce(query, { getOptions }); return [ (value ? value.docs.map(doc => snapshotToData(doc, idField)) diff --git a/firestore/useDocument.ts b/firestore/useDocument.ts index e01c47c..b8531a8 100644 --- a/firestore/useDocument.ts +++ b/firestore/useDocument.ts @@ -1,20 +1,20 @@ -import { firestore, FirebaseError } from 'firebase'; +import { firestore } from 'firebase'; import { useEffect } from 'react'; -import { snapshotToData, transformError } from './helpers'; +import { snapshotToData } from './helpers'; import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export type DocumentHook = LoadingHook< - firestore.DocumentSnapshot, - FirebaseError ->; -export type DocumentDataHook = LoadingHook; +export type DocumentHook = LoadingHook; +export type DocumentDataHook = LoadingHook; export const useDocument = ( docRef?: firestore.DocumentReference | null, - options?: firestore.SnapshotListenOptions + options?: { + snapshotListenOptions?: firestore.SnapshotListenOptions; + } ): DocumentHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< - firestore.DocumentSnapshot + firestore.DocumentSnapshot, + Error >(); const ref = useIsEqualRef(docRef, reset); @@ -24,13 +24,14 @@ export const useDocument = ( setValue(undefined); return; } - const listener = options - ? ref.current.onSnapshot(options, setValue, (error: Error) => - setError(transformError(error)) - ) - : ref.current.onSnapshot(setValue, (error: Error) => - setError(transformError(error)) - ); + const listener = + options && options.snapshotListenOptions + ? ref.current.onSnapshot( + options.snapshotListenOptions, + setValue, + setError + ) + : ref.current.onSnapshot(setValue, setError); return () => { listener(); @@ -53,7 +54,9 @@ export const useDocumentData = ( const snapshotListenOptions = options ? options.snapshotListenOptions : undefined; - const [value, loading, error] = useDocument(docRef, snapshotListenOptions); + const [value, loading, error] = useDocument(docRef, { + snapshotListenOptions, + }); return [ (value ? snapshotToData(value, idField) : undefined) as T, loading, diff --git a/firestore/useDocumentOnce.ts b/firestore/useDocumentOnce.ts index eb0c31e..7ca2e63 100644 --- a/firestore/useDocumentOnce.ts +++ b/firestore/useDocumentOnce.ts @@ -1,20 +1,20 @@ -import { firestore, FirebaseError } from 'firebase'; +import { firestore } from 'firebase'; import { useEffect } from 'react'; import { snapshotToData } from './helpers'; import { LoadingHook, useIsEqualRef, useLoadingValue } from '../util'; -export type DocumentOnceHook = LoadingHook< - firestore.DocumentSnapshot, - FirebaseError ->; -export type DocumentDataOnceHook = LoadingHook; +export type DocumentOnceHook = LoadingHook; +export type DocumentDataOnceHook = LoadingHook; export const useDocumentOnce = ( docRef?: firestore.DocumentReference | null, - options?: firestore.GetOptions + options?: { + getOptions?: firestore.GetOptions; + } ): DocumentOnceHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< - firestore.DocumentSnapshot + firestore.DocumentSnapshot, + Error >(); const ref = useIsEqualRef(docRef, reset); @@ -25,7 +25,7 @@ export const useDocumentOnce = ( return; } ref.current - .get(options) + .get(options ? options.getOptions : undefined) .then(setValue) .catch(setError); }, @@ -44,7 +44,7 @@ export const useDocumentDataOnce = ( ): DocumentDataOnceHook => { const idField = options ? options.idField : undefined; const getOptions = options ? options.getOptions : undefined; - const [value, loading, error] = useDocumentOnce(docRef, getOptions); + const [value, loading, error] = useDocumentOnce(docRef, { getOptions }); return [ (value ? snapshotToData(value, idField) : undefined) as T, loading, diff --git a/package.json b/package.json index 74f86cd..f232369 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.4", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", diff --git a/storage/README.md b/storage/README.md index 2acaecb..78f3b2f 100644 --- a/storage/README.md +++ b/storage/README.md @@ -22,12 +22,18 @@ List of Cloud Storage hooks: const [downloadUrl, loading, error] = useDownloadURL(reference); ``` -Returns the download URL as a `string` (if a reference is supplied), a boolean to indicate whether the the download URL is still being loaded and any `firebase.FirebaseError` returned by Firebase when trying to load the download URL. +Retrieve the download URL for a storage reference. The `useDownloadURL` hook takes the following parameters: - `reference`: (optional) `firebase.storage.Reference` that you would like the download URL for +Returns: + +- `downloadUrl`: A `string` download URL, or `void` if no storage reference is supplied +- `loading`: A `boolean` to indicate whether the the download URL is still being loaded +- `error`: Any `firebase.FirebaseError` returned by Firebase when trying to load the user, or `void` if there is no error + #### Full example ```js diff --git a/storage/useDownloadURL.ts b/storage/useDownloadURL.ts index b0e7eb3..f6b781e 100644 --- a/storage/useDownloadURL.ts +++ b/storage/useDownloadURL.ts @@ -6,7 +6,8 @@ export type DownloadURLHook = LoadingHook; export default (storageRef?: storage.Reference | null): DownloadURLHook => { const { error, loading, reset, setError, setValue, value } = useLoadingValue< - string + string, + FirebaseError >(); const ref = useComparatorRef(storageRef, isEqual, reset); diff --git a/util/useLoadingValue.ts b/util/useLoadingValue.ts index c9d3650..3de416e 100644 --- a/util/useLoadingValue.ts +++ b/util/useLoadingValue.ts @@ -1,25 +1,24 @@ import { useReducer } from 'react'; -import { FirebaseError } from 'firebase'; -export type LoadingValue = { - error?: FirebaseError; +export type LoadingValue = { + error?: E; loading: boolean; reset: () => void; - setError: (error: FirebaseError) => void; + setError: (error: E) => void; setValue: (value?: T | null) => void; value?: T; }; -type ReducerState = { - error?: FirebaseError; +type ReducerState = { + error?: E; loading: boolean; value?: any; }; -type ErrorAction = { type: 'error'; error: FirebaseError }; +type ErrorAction = { type: 'error'; error: E }; type ResetAction = { type: 'reset'; defaultValue?: any }; type ValueAction = { type: 'value'; value: any }; -type ReducerAction = ErrorAction | ResetAction | ValueAction; +type ReducerAction = ErrorAction | ResetAction | ValueAction; const defaultState = (defaultValue?: any) => { return { @@ -28,7 +27,10 @@ const defaultState = (defaultValue?: any) => { }; }; -const reducer = (state: ReducerState, action: ReducerAction): ReducerState => { +const reducer = () => ( + state: ReducerState, + action: ReducerAction +): ReducerState => { switch (action.type) { case 'error': return { @@ -49,16 +51,19 @@ const reducer = (state: ReducerState, action: ReducerAction): ReducerState => { } }; -export default (getDefaultValue?: () => T | null): LoadingValue => { +export default (getDefaultValue?: () => T | null): LoadingValue => { const defaultValue = getDefaultValue ? getDefaultValue() : undefined; - const [state, dispatch] = useReducer(reducer, defaultState(defaultValue)); + const [state, dispatch] = useReducer( + reducer(), + defaultState(defaultValue) + ); const reset = () => { const defaultValue = getDefaultValue ? getDefaultValue() : undefined; dispatch({ type: 'reset', defaultValue }); }; - const setError = (error: FirebaseError) => { + const setError = (error: E) => { dispatch({ type: 'error', error }); }; From 5f2a16721562e9addff42b5d26776b2a0ea26e1b Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Thu, 16 May 2019 15:37:33 +0100 Subject: [PATCH 09/12] v2.0.0-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f232369..4351a15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-firebase-hooks", - "version": "2.0.0-alpha.4", + "version": "2.0.0-rc.1", "description": "React Hooks for Firebase", "author": "CS Frequency Limited (https://csfrequency.com)", "license": "Apache-2.0", From 288519490e4b81bbee8b1dad51e4e782c7128890 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Thu, 16 May 2019 15:43:43 +0100 Subject: [PATCH 10/12] Update README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5135d65..c62c467 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ -# React Firebase Hooks +# React Firebase Hooks (v2) -A set of reusable React Hooks for [Firebase](https://firebase.google.com/). +A set of reusable [React Hooks](https://reactjs.org/docs/hooks-intro.html) for [Firebase](https://firebase.google.com/). [![npm version](https://img.shields.io/npm/v/react-firebase-hooks.svg?style=flat-square)](https://www.npmjs.com/package/react-firebase-hooks) [![npm downloads](https://img.shields.io/npm/dm/react-firebase-hooks.svg?style=flat-square)](https://www.npmjs.com/package/react-firebase-hooks) -> [Hooks](https://reactjs.org/docs/hooks-intro.html) are a new feature that lets you use state and other React features without writing a class and are available in React v16.8.0 or later. +> Official support for Hooks was added to React Native in v0.59.0. React Firebase Hooks works with both the Firebase JS SDK and React Native Firebase, although some of the Flow and Typescript typings may be incorrect - we are investigating ways to improve this for React Native Firebase users. -> Official support for Hooks was added to React Native in v0.59.0. React Firebase Hooks works with both the Firebase JS SDK and React Native Firebase, although some of the Flow and Typescript typings may be incorrect - we are investigating ways to improve this for React Native users. +**This documentation is for v2 of React Firebase Hooks which is currently at the Release Candidate stage for testing. For v1 documentation, click [here](https://github.com/CSFrequency/react-firebase-hooks/tree/v1.2.1).** ## Installation React Firebase Hooks requires **React 16.8.0 or later** and **Firebase v5.0.0 or later**. ``` -npm install --save react-firebase-hooks +npm install --save react-firebase-hooks@next ``` This assumes that you’re using the [npm](https://npmjs.com) package manager with a module bundler like [Webpack](https://webpack.js.org/) or [Browserify](http://browserify.org/) to consume [CommonJS](http://webpack.github.io/docs/commonjs.html) modules. From 4d20575da267ea56bb9f54ef8aa60e7290142e98 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 21 May 2019 08:38:50 +0100 Subject: [PATCH 11/12] Update README for release --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c62c467..7a840ed 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# React Firebase Hooks (v2) +# React Firebase Hooks A set of reusable [React Hooks](https://reactjs.org/docs/hooks-intro.html) for [Firebase](https://firebase.google.com/). @@ -7,14 +7,14 @@ A set of reusable [React Hooks](https://reactjs.org/docs/hooks-intro.html) for [ > Official support for Hooks was added to React Native in v0.59.0. React Firebase Hooks works with both the Firebase JS SDK and React Native Firebase, although some of the Flow and Typescript typings may be incorrect - we are investigating ways to improve this for React Native Firebase users. -**This documentation is for v2 of React Firebase Hooks which is currently at the Release Candidate stage for testing. For v1 documentation, click [here](https://github.com/CSFrequency/react-firebase-hooks/tree/v1.2.1).** +**This documentation is for v2 of React Firebase Hooks which involved a number of breaking changes, including switching from `Object` to `Array` returns - more details [here](https://github.com/CSFrequency/react-firebase-hooks/releases/tag/v2.0.0). For v1 documentation, see [here](https://github.com/CSFrequency/react-firebase-hooks/tree/v1.2.1).** ## Installation React Firebase Hooks requires **React 16.8.0 or later** and **Firebase v5.0.0 or later**. ``` -npm install --save react-firebase-hooks@next +npm install --save react-firebase-hooks ``` This assumes that you’re using the [npm](https://npmjs.com) package manager with a module bundler like [Webpack](https://webpack.js.org/) or [Browserify](http://browserify.org/) to consume [CommonJS](http://webpack.github.io/docs/commonjs.html) modules. From 862b68cfce97c810db0a73f88d9672291746ae96 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Tue, 21 May 2019 08:44:54 +0100 Subject: [PATCH 12/12] Fix merge --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 19b59fb..a5f79da 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# React Firebase Hooks - v1 +# React Firebase Hooks A set of reusable [React Hooks](https://reactjs.org/docs/hooks-intro.html) for [Firebase](https://firebase.google.com/). @@ -9,8 +9,6 @@ A set of reusable [React Hooks](https://reactjs.org/docs/hooks-intro.html) for [ **This documentation is for v2 of React Firebase Hooks which involved a number of breaking changes, including switching from `Object` to `Array` returns - more details [here](https://github.com/CSFrequency/react-firebase-hooks/releases/tag/v2.0.0). For v1 documentation, see [here](https://github.com/CSFrequency/react-firebase-hooks/tree/v1.2.1).** -**This documentation is for v1 of React Firebase Hooks which is an old version. If you'd like to switch to v2, check out the [Release Notes](https://github.com/CSFrequency/react-firebase-hooks/releases/tag/v2.0.0) and [Updated Documentation](https://github.com/CSFrequency/react-firebase-hooks).** - ## Installation React Firebase Hooks requires **React 16.8.0 or later** and **Firebase v5.0.0 or later**.