From 4d5e5079a4ed1ca1772a8aca0ab16dfd332f0234 Mon Sep 17 00:00:00 2001 From: "Eray Erdin (&mut self)" Date: Sat, 10 Feb 2024 17:25:02 +0300 Subject: [PATCH] Make Parameters for Firestore Hooks Positional (#73) * convert parameters to positional for useDocument hook * add and impl UseDocumentOptions.listenToMetadataChanges * fix useDocument declaration * convert parameters into positional for useCollection hook * add UseCollectionOptions.listenToMetadataChanges * convert into positional parameters for useAddDocument hook * convert parameters into positional for useDeleteDocument * convert parameters into positional for useSetDocument hook --- docs/hooks/useCollection.md | 9 ++++----- docs/hooks/useDocument.md | 9 ++++----- src/firestore/FirestoreDocument.tsx | 2 +- src/firestore/useAddDocument.test.ts | 14 +++++++------- src/firestore/useAddDocument.ts | 10 +++------- src/firestore/useCollection.test.ts | 12 ++++++------ src/firestore/useCollection.ts | 25 ++++++++++++------------- src/firestore/useDeleteDocument.test.ts | 20 +++++--------------- src/firestore/useDeleteDocument.ts | 10 +++------- src/firestore/useDocument.test.ts | 20 ++++++++------------ src/firestore/useDocument.ts | 25 ++++++++++++------------- src/firestore/useSetDocument.test.ts | 12 ++++++------ src/firestore/useSetDocument.ts | 10 +++------- 13 files changed, 74 insertions(+), 104 deletions(-) diff --git a/docs/hooks/useCollection.md b/docs/hooks/useCollection.md index ef4942f..029a827 100644 --- a/docs/hooks/useCollection.md +++ b/docs/hooks/useCollection.md @@ -33,15 +33,13 @@ Input parameters for `useCollection` hook is as follows: | Name | Type | Description | Required | Default Value | |---|---|---|---|---| | `reference` | [`firebase/firestore/CollectionRefference`][CollectionReferenceRefDoc] or [`firebase/firestore/Query`][QueryRefDoc] | Reference to a collection in Firestore or a query. | ✅ | - | -| `options` | Object | Options for the hook. | ❌ | `{ listen: false }` | +| `options` | Object | Options for the hook. | ❌ | See below. | | `options.listen` | `boolean` | Whether to listen to realtime changes of the document or not. | ❌ | `false` | +| `options.listenToMetadataChanges` | `boolean` | Whether to listen to realtime changes of the document or not as well as its metadata. See [this][EventsForMetadataChangesDoc]. | ❌ | `false` | !!! note `options.listen` is `false` by default to prevent unnecessary READ queries from Firestore. -!!! warning - When `options.listen` is `true`, the change is reflected including the metadata changes on the document. See [this page](https://firebase.google.com/docs/firestore/query-data/listen#events-metadata-changes) to understand `includeMetadataChanges` option in Firestore. - ## Return Type `useCollection` hook returns an object with properties as below: @@ -58,4 +56,5 @@ Input parameters for `useCollection` hook is as follows: [CollectionReferenceRefDoc]: https://firebase.google.com/docs/reference/node/firebase.firestore.CollectionReference [QueryRefDoc]: https://firebase.google.com/docs/reference/node/firebase.database.Query [QuerySnapshotRefDoc]: https://firebase.google.com/docs/reference/node/firebase.firestore.QuerySnapshot -[FirebaseErrorRefDoc]: https://firebase.google.com/docs/reference/node/firebase.FirebaseError \ No newline at end of file +[FirebaseErrorRefDoc]: https://firebase.google.com/docs/reference/node/firebase.FirebaseError +[EventsForMetadataChangesDoc]: https://firebase.google.com/docs/firestore/query-data/listen#events-metadata-changes \ No newline at end of file diff --git a/docs/hooks/useDocument.md b/docs/hooks/useDocument.md index 5cfc077..cbdd52b 100644 --- a/docs/hooks/useDocument.md +++ b/docs/hooks/useDocument.md @@ -25,15 +25,13 @@ Input parameters for `useDocument` hook is as follows: | Name | Type | Description | Required | Default Value | |---|---|---|---|---| | `reference` | [`firebase/firestore/DocumentReference`][DocumentReferenceRefDoc] | Reference to a document in Firestore. | ✅ | - | -| `options` | Object | Options for the hook. | ❌ | `{ listen: false }` | +| `options` | Object | Options for the hook. | ❌ | See the following parameters. | | `options.listen` | `boolean` | Whether to listen to realtime changes of the document or not. | ❌ | `false` | +| `options.listenToMetadataChanges` | `boolean` | Whether to listen to realtime changes of the document as well its metadata. See [here][EventsForMetadataChangesDoc] | ❌ | `false` | !!! note `options.listen` is `false` by default to prevent unnecessary READ queries from Firestore. -!!! warning - When `options.listen` is `true`, the change is reflected including the metadata changes on the document. See [this page](https://firebase.google.com/docs/firestore/query-data/listen#events-metadata-changes) to understand `includeMetadataChanges` option in Firestore. - ## Return Type `useDocument` hook returns an object with properties as below: @@ -49,4 +47,5 @@ Input parameters for `useDocument` hook is as follows: [DocumentReferenceRefDoc]: https://firebase.google.com/docs/reference/node/firebase.firestore.DocumentReference [DocumentSnapshotRefDoc]: https://firebase.google.com/docs/reference/node/firebase.firestore.DocumentSnapshot -[FirebaseErrorRefDoc]: https://firebase.google.com/docs/reference/node/firebase.FirebaseError \ No newline at end of file +[FirebaseErrorRefDoc]: https://firebase.google.com/docs/reference/node/firebase.FirebaseError +[EventsForMetadataChangesDoc]: https://firebase.google.com/docs/firestore/query-data/listen#events-metadata-changes \ No newline at end of file diff --git a/src/firestore/FirestoreDocument.tsx b/src/firestore/FirestoreDocument.tsx index 6eb4073..5a51f86 100644 --- a/src/firestore/FirestoreDocument.tsx +++ b/src/firestore/FirestoreDocument.tsx @@ -28,7 +28,7 @@ export const FirestoreDocument = ({ loading: processing, snapshot, error: err, - } = useDocument({ reference, options: { listen } }); + } = useDocument(reference, { listen }); return processing ? onLoading() : err ? onError(err) : onDone(snapshot!); }; diff --git a/src/firestore/useAddDocument.test.ts b/src/firestore/useAddDocument.test.ts index 07a4396..2ba15ef 100644 --- a/src/firestore/useAddDocument.test.ts +++ b/src/firestore/useAddDocument.test.ts @@ -19,7 +19,7 @@ describe("initially useAddDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useAddDocument({ reference: colRef })); + const { result } = renderHook(() => useAddDocument(colRef)); const { state } = result.current; expect(state).toBe("ready"); @@ -32,7 +32,7 @@ describe("initially useAddDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useAddDocument({ reference: colRef })); + const { result } = renderHook(() => useAddDocument(colRef)); const { reference } = result.current; expect(reference).toBeUndefined(); @@ -45,7 +45,7 @@ describe("initially useAddDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useAddDocument({ reference: colRef })); + const { result } = renderHook(() => useAddDocument(colRef)); const { error } = result.current; expect(error).toBeUndefined(); @@ -61,7 +61,7 @@ describe("as soon as dispatched, useAddDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useAddDocument({ reference: colRef })); + const { result } = renderHook(() => useAddDocument(colRef)); const { dispatch } = result.current; dispatch(docData); await sleep(30); @@ -80,7 +80,7 @@ describe("after dispatched, useAddDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useAddDocument({ reference: colRef })); + const { result } = renderHook(() => useAddDocument(colRef)); const { dispatch } = result.current; await dispatch(docData); await sleep(250); @@ -97,7 +97,7 @@ describe("after dispatched, useAddDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useAddDocument({ reference: colRef })); + const { result } = renderHook(() => useAddDocument(colRef)); const { dispatch } = result.current; await dispatch(docData); await sleep(250); @@ -114,7 +114,7 @@ describe("after dispatched, useAddDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useAddDocument({ reference: colRef })); + const { result } = renderHook(() => useAddDocument(colRef)); const { dispatch } = result.current; await dispatch(docData); await sleep(250); diff --git a/src/firestore/useAddDocument.ts b/src/firestore/useAddDocument.ts index 70a09fa..115f24f 100644 --- a/src/firestore/useAddDocument.ts +++ b/src/firestore/useAddDocument.ts @@ -12,10 +12,6 @@ import { } from "firebase/firestore"; import { useState } from "react"; -type UseAddDocumentParams = { - reference: CollectionReference; -}; - type UseAddDocumentState = "ready" | "loading" | "done"; type UseAddDocumentDispatcher = ( data: DocumentData, @@ -28,9 +24,9 @@ type UseAddDocument = { error?: FirebaseError; }; -export const useAddDocument = ({ - reference, -}: UseAddDocumentParams): UseAddDocument => { +export const useAddDocument = ( + reference: CollectionReference, +): UseAddDocument => { const [state, setState] = useState("ready"); const [refer, setRefer] = useState(); const [error, setError] = useState(); diff --git a/src/firestore/useCollection.test.ts b/src/firestore/useCollection.test.ts index 2c53ba6..1caee38 100644 --- a/src/firestore/useCollection.test.ts +++ b/src/firestore/useCollection.test.ts @@ -20,7 +20,7 @@ describe("initially useCollection hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useCollection({ query: colRef })); + const { result } = renderHook(() => useCollection(colRef)); const { loading } = result.current; expect(loading).toBe(true); @@ -34,7 +34,7 @@ describe("initially useCollection hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useCollection({ query: colRef })); + const { result } = renderHook(() => useCollection(colRef)); const { snapshot } = result.current; expect(snapshot).toBeUndefined(); @@ -48,7 +48,7 @@ describe("initially useCollection hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useCollection({ query: colRef })); + const { result } = renderHook(() => useCollection(colRef)); const { error } = result.current; expect(error).toBeUndefined(); @@ -64,7 +64,7 @@ describe("later useCollection hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useCollection({ query: colRef })); + const { result } = renderHook(() => useCollection(colRef)); await sleep(250); const { loading } = result.current; expect(loading).toBe(false); @@ -79,7 +79,7 @@ describe("later useCollection hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useCollection({ query: colRef })); + const { result } = renderHook(() => useCollection(colRef)); await sleep(250); const { snapshot } = result.current; expect(snapshot?.size).toBeGreaterThan(0); @@ -94,7 +94,7 @@ describe("later useCollection hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useCollection({ query: colRef })); + const { result } = renderHook(() => useCollection(colRef)); await sleep(250); const { error } = result.current; expect(error).toBeUndefined(); diff --git a/src/firestore/useCollection.ts b/src/firestore/useCollection.ts index 6e4f3d4..bd59b0d 100644 --- a/src/firestore/useCollection.ts +++ b/src/firestore/useCollection.ts @@ -7,11 +7,9 @@ import { FirebaseError } from "firebase/app"; import { Query, QuerySnapshot, getDocs, onSnapshot } from "firebase/firestore"; import { useEffect, useState } from "react"; -type UseCollectionParams = { - query: Query; - options?: { - listen: boolean; - }; +type UseCollectionOptions = { + listen?: boolean; + listenToMetadataChanges?: boolean; }; type UseCollection = { @@ -20,12 +18,13 @@ type UseCollection = { error?: FirebaseError; }; -export const useCollection = ({ - query, - options = { listen: false }, -}: UseCollectionParams): UseCollection => { - const { listen } = options; - +export const useCollection = ( + query: Query, + { listen, listenToMetadataChanges }: UseCollectionOptions = { + listen: false, + listenToMetadataChanges: false, + }, +): UseCollection => { const [loading, setLoading] = useState(true); const [snapshot, setSnapshot] = useState(); const [error, setError] = useState(); @@ -36,7 +35,7 @@ export const useCollection = ({ if (listen) { const unsub = onSnapshot( query, - { includeMetadataChanges: true }, + { includeMetadataChanges: listenToMetadataChanges }, (snap) => { setSnapshot(snap); setLoading(false); @@ -62,7 +61,7 @@ export const useCollection = ({ }) .finally(() => setLoading(false)); } - }, [listen, query]); + }, [query, listen, listenToMetadataChanges]); return { loading, snapshot, error }; }; diff --git a/src/firestore/useDeleteDocument.test.ts b/src/firestore/useDeleteDocument.test.ts index 999c167..90f7784 100644 --- a/src/firestore/useDeleteDocument.test.ts +++ b/src/firestore/useDeleteDocument.test.ts @@ -17,9 +17,7 @@ describe("initially useDeleteDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => - useDeleteDocument({ reference: docRef }), - ); + const { result } = renderHook(() => useDeleteDocument(docRef)); const { state } = result.current; expect(state).toBe("ready"); @@ -32,9 +30,7 @@ describe("initially useDeleteDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => - useDeleteDocument({ reference: docRef }), - ); + const { result } = renderHook(() => useDeleteDocument(docRef)); const { error } = result.current; expect(error).toBeUndefined(); @@ -50,9 +46,7 @@ describe("as soon as dispatched, useDeleteDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => - useDeleteDocument({ reference: docRef }), - ); + const { result } = renderHook(() => useDeleteDocument(docRef)); const { dispatch } = result.current; dispatch(); await sleep(30); @@ -71,9 +65,7 @@ describe("after dispatched, useDeleteDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => - useDeleteDocument({ reference: docRef }), - ); + const { result } = renderHook(() => useDeleteDocument(docRef)); const { dispatch } = result.current; await dispatch(); await sleep(250); @@ -90,9 +82,7 @@ describe("after dispatched, useDeleteDocument hook", () => { await setDoc(docRef, { displayName: "Use Delete Document" }); // test - const { result } = renderHook(() => - useDeleteDocument({ reference: docRef }), - ); + const { result } = renderHook(() => useDeleteDocument(docRef)); const { dispatch } = result.current; await dispatch(); await sleep(250); diff --git a/src/firestore/useDeleteDocument.ts b/src/firestore/useDeleteDocument.ts index a00621e..b65e56f 100644 --- a/src/firestore/useDeleteDocument.ts +++ b/src/firestore/useDeleteDocument.ts @@ -7,10 +7,6 @@ import { FirebaseError } from "firebase/app"; import { DocumentReference, deleteDoc } from "firebase/firestore"; import { useState } from "react"; -type UseSetDocumentParams = { - reference: DocumentReference; -}; - type UseSetDocumentState = "ready" | "loading" | "done"; type UseSetDocumentDispatcher = () => Promise; @@ -20,9 +16,9 @@ type UseSetDocument = { error?: FirebaseError; }; -export const useDeleteDocument = ({ - reference, -}: UseSetDocumentParams): UseSetDocument => { +export const useDeleteDocument = ( + reference: DocumentReference, +): UseSetDocument => { const [state, setState] = useState("ready"); const [error, setError] = useState(); diff --git a/src/firestore/useDocument.test.ts b/src/firestore/useDocument.test.ts index 328d464..e76a4f0 100644 --- a/src/firestore/useDocument.test.ts +++ b/src/firestore/useDocument.test.ts @@ -19,7 +19,7 @@ describe("initially useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useDocument({ reference: docRef })); + const { result } = renderHook(() => useDocument(docRef)); const { loading } = result.current; expect(loading).toBe(true); @@ -33,7 +33,7 @@ describe("initially useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useDocument({ reference: docRef })); + const { result } = renderHook(() => useDocument(docRef)); const { snapshot } = result.current; expect(snapshot).toBeUndefined(); @@ -47,7 +47,7 @@ describe("initially useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useDocument({ reference: docRef })); + const { result } = renderHook(() => useDocument(docRef)); const { error } = result.current; expect(error).toBeUndefined(); @@ -63,7 +63,7 @@ describe("later useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useDocument({ reference: docRef })); + const { result } = renderHook(() => useDocument(docRef)); await sleep(250); const { loading } = result.current; expect(loading).toBe(false); @@ -78,7 +78,7 @@ describe("later useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useDocument({ reference: docRef })); + const { result } = renderHook(() => useDocument(docRef)); await sleep(250); const { snapshot } = result.current; expect(snapshot?.data()).toStrictEqual(docData); @@ -93,7 +93,7 @@ describe("later useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => useDocument({ reference: docRef })); + const { result } = renderHook(() => useDocument(docRef)); await sleep(250); const { error } = result.current; expect(error).toBeUndefined(); @@ -110,9 +110,7 @@ describe.skip("later listen useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => - useDocument({ reference: docRef, options: { listen: true } }), - ); + const { result } = renderHook(() => useDocument(docRef, { listen: true })); await sleep(250); const { loading } = result.current; expect(loading).toBe(false); @@ -127,9 +125,7 @@ describe.skip("later listen useDocument hook", () => { await setDoc(docRef, docData); // test - const { result } = renderHook(() => - useDocument({ reference: docRef, options: { listen: true } }), - ); + const { result } = renderHook(() => useDocument(docRef, { listen: true })); await sleep(250); const { snapshot } = result.current; expect(snapshot?.data()).toStrictEqual(docData); diff --git a/src/firestore/useDocument.ts b/src/firestore/useDocument.ts index 2cce960..de2d3e0 100644 --- a/src/firestore/useDocument.ts +++ b/src/firestore/useDocument.ts @@ -12,11 +12,9 @@ import { } from "firebase/firestore"; import { useEffect, useState } from "react"; -type UseDocumentParams = { - reference: DocumentReference; - options?: { - listen: boolean; - }; +type UseDocumentOptions = { + listen?: boolean; + listenToMetadataChanges?: boolean; }; type UseDocument = { @@ -25,12 +23,13 @@ type UseDocument = { error?: FirebaseError; }; -export const useDocument = ({ - reference, - options = { listen: false }, -}: UseDocumentParams): UseDocument => { - const { listen } = options; - +export const useDocument = ( + reference: DocumentReference, + { listen, listenToMetadataChanges }: UseDocumentOptions = { + listen: true, + listenToMetadataChanges: false, + }, +): UseDocument => { const [loading, setLoading] = useState(true); const [snapshot, setSnapshot] = useState(); const [error, setError] = useState(); @@ -40,7 +39,7 @@ export const useDocument = ({ if (listen) { const unsub = onSnapshot( reference, - { includeMetadataChanges: true }, + { includeMetadataChanges: listenToMetadataChanges }, (snap) => { setSnapshot(snap); setLoading(false); @@ -66,7 +65,7 @@ export const useDocument = ({ }) .finally(() => setLoading(false)); } - }, [listen, reference]); + }, [reference, listen, listenToMetadataChanges]); return { loading, snapshot, error }; }; diff --git a/src/firestore/useSetDocument.test.ts b/src/firestore/useSetDocument.test.ts index 875882b..dcd9300 100644 --- a/src/firestore/useSetDocument.test.ts +++ b/src/firestore/useSetDocument.test.ts @@ -18,7 +18,7 @@ describe("initially useSetDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useSetDocument({ reference: docRef })); + const { result } = renderHook(() => useSetDocument(docRef)); const { state } = result.current; expect(state).toBe("ready"); @@ -31,7 +31,7 @@ describe("initially useSetDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useSetDocument({ reference: docRef })); + const { result } = renderHook(() => useSetDocument(docRef)); const { error } = result.current; expect(error).toBeUndefined(); @@ -47,7 +47,7 @@ describe("as soon as dispatched, useSetDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useSetDocument({ reference: docRef })); + const { result } = renderHook(() => useSetDocument(docRef)); const { dispatch } = result.current; dispatch(docData); await sleep(30); @@ -66,7 +66,7 @@ describe("after dispatched, useSetDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useSetDocument({ reference: docRef })); + const { result } = renderHook(() => useSetDocument(docRef)); const { dispatch } = result.current; await dispatch(docData); await sleep(250); @@ -83,7 +83,7 @@ describe("after dispatched, useSetDocument hook", () => { await deleteDoc(docRef); // test - const { result } = renderHook(() => useSetDocument({ reference: docRef })); + const { result } = renderHook(() => useSetDocument(docRef)); const { dispatch } = result.current; await dispatch(docData); await sleep(250); @@ -101,7 +101,7 @@ describe("after dispatched, useSetDocument hook", () => { await setDoc(docRef, { displayName: "Use Set Document" }); // test - const { result } = renderHook(() => useSetDocument({ reference: docRef })); + const { result } = renderHook(() => useSetDocument(docRef)); const { dispatch } = result.current; await dispatch({ username: "useSetDocument" }, { merge: true }); await sleep(250); diff --git a/src/firestore/useSetDocument.ts b/src/firestore/useSetDocument.ts index 5c1c26b..dba6c17 100644 --- a/src/firestore/useSetDocument.ts +++ b/src/firestore/useSetDocument.ts @@ -12,10 +12,6 @@ import { } from "firebase/firestore"; import { useState } from "react"; -type UseSetDocumentParams = { - reference: DocumentReference; -}; - type UseSetDocumentState = "ready" | "loading" | "done"; type UseSetDocumentDispatcher = ( data: DocumentData, @@ -28,9 +24,9 @@ type UseSetDocument = { error?: FirebaseError; }; -export const useSetDocument = ({ - reference, -}: UseSetDocumentParams): UseSetDocument => { +export const useSetDocument = ( + reference: DocumentReference, +): UseSetDocument => { const [state, setState] = useState("ready"); const [error, setError] = useState();