usefirestoreDocWithAtom: memoize docRef

This commit is contained in:
Sidney Alcantara
2022-05-30 11:59:11 +10:00
parent 3daa1b413f
commit ba3531d93e

View File

@@ -1,9 +1,12 @@
import { useEffect } from "react";
import useMemoValue from "use-memo-value";
import { useAtom, PrimitiveAtom, useSetAtom } from "jotai";
import { Scope } from "jotai/core/atom";
import { set } from "lodash-es";
import {
Firestore,
doc,
refEqual,
onSnapshot,
FirestoreError,
setDoc,
@@ -46,14 +49,6 @@ export function useFirestoreDocWithAtom<T = TableRow>(
path: string | undefined,
options?: IUseFirestoreDocWithAtomOptions<T>
) {
const [firebaseDb] = useAtom(firebaseDbAtom, globalScope);
const setDataAtom = useSetAtom(dataAtom, dataScope);
const setUpdateDataAtom = useSetAtom(
options?.updateDataAtom || (dataAtom as any),
dataScope
);
const handleError = useErrorHandler();
// Destructure options so they can be used as useEffect dependencies
const {
pathSegments,
@@ -63,28 +58,37 @@ export function useFirestoreDocWithAtom<T = TableRow>(
updateDataAtom,
} = options || {};
useEffect(() => {
if (!path || (Array.isArray(pathSegments) && pathSegments.some((x) => !x)))
return;
const [firebaseDb] = useAtom(firebaseDbAtom, globalScope);
const setDataAtom = useSetAtom(dataAtom, dataScope);
const setUpdateDataAtom = useSetAtom(
options?.updateDataAtom || (dataAtom as any),
dataScope
);
const handleError = useErrorHandler();
let suspended = false;
// Create the doc ref and memoize using Firestores refEqual
const memoizedDocRef = useMemoValue(
getDocRef<T>(firebaseDb, path, pathSegments),
(next, prev) => refEqual(next as any, prev as any)
);
useEffect(() => {
// If path is invalid and no memoizedDocRef was created, dont continue
if (!memoizedDocRef) return;
// Suspend data atom until we get the first snapshot
let suspended = false;
if (!disableSuspense) {
setDataAtom(new Promise(() => {}) as unknown as T);
suspended = true;
}
const ref = doc(
firebaseDb,
path,
...((pathSegments as string[]) || [])
) as DocumentReference<T>;
// Create a listener for the document
const unsubscribe = onSnapshot(
ref,
memoizedDocRef,
(docSnapshot) => {
try {
// Create doc if it doesnt exist
if (!docSnapshot.exists() && !!createIfNonExistent) {
setDoc(docSnapshot.ref, createIfNonExistent);
setDataAtom({ ...createIfNonExistent, _rowy_ref: docSnapshot.ref });
@@ -107,6 +111,27 @@ export function useFirestoreDocWithAtom<T = TableRow>(
}
);
// When the listener will change, unsubscribe
return () => {
unsubscribe();
};
}, [
memoizedDocRef,
onError,
setDataAtom,
disableSuspense,
createIfNonExistent,
handleError,
updateDataAtom,
setUpdateDataAtom,
]);
// Set updateDocAtom and deleteDocAtom values if they exist
useEffect(() => {
// If path is invalid and no memoizedDocRef was created,
// dont set update and delete atoms
if (!memoizedDocRef) return;
// If `updateDataAtom` was passed,
// set the atoms value to a function that updates the document
if (updateDataAtom) {
@@ -119,28 +144,35 @@ export function useFirestoreDocWithAtom<T = TableRow>(
}
}
return setDoc(ref, updateToDb, { merge: true });
return setDoc(memoizedDocRef, updateToDb, { merge: true });
});
}
return () => {
unsubscribe();
// If `options?.updateDataAtom` was passed,
// If `updateDataAtom` was passed,
// reset the atoms value to prevent writes
if (updateDataAtom) setUpdateDataAtom(undefined);
};
}, [
firebaseDb,
path,
pathSegments,
onError,
setDataAtom,
disableSuspense,
createIfNonExistent,
handleError,
updateDataAtom,
setUpdateDataAtom,
]);
}, [memoizedDocRef, updateDataAtom, setUpdateDataAtom]);
}
export default useFirestoreDocWithAtom;
/**
* Create the Firestore document reference.
* Put code in a function so the results can be compared by useMemoValue.
*/
const getDocRef = <T>(
firebaseDb: Firestore,
path: string | undefined,
pathSegments?: Array<string | undefined>
) => {
if (!path || (Array.isArray(pathSegments) && pathSegments.some((x) => !x)))
return null;
return doc(
firebaseDb,
path,
...((pathSegments as string[]) || [])
) as DocumentReference<T>;
};