react-firebase-hooks icon indicating copy to clipboard operation
react-firebase-hooks copied to clipboard

useDocumentData fails to update when read permission is not longer denied

Open amiregelz opened this issue 2 years ago • 2 comments

I'm using useDocumentData in order to fetch a document and keep it updated.

When the hook is initialized, reading the doc is not permitted yet (due to a certain condition in the Firestore Security Rules not being met), therefor the read fails with FirebaseError: Missing or insufficient permissions.

However, after a few minutes there is a change in the database and the document can be read successfully.

If I refresh the page it works properly - otherwise the hook does not automatically update.

How can I fix that? I need it to automatically detect that there are permissions to read this document and keep me up to date with its data.

amiregelz avatar Jan 26 '23 18:01 amiregelz

I think that can be done by writing a query with the ID of the target document as a filter like below.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /tests/{id} {
      allow read: if resource.data.isPrivate == false;
      allow write: if true;
    }
  }
}
import {
  documentId,
  DocumentReference,
  query,
  setDoc,
  where,
} from "firebase/firestore";
import { useCollectionData } from "react-firebase-hooks/firestore";

export const Test = (docRef: DocumentReference) => {
  const [docs] = useCollectionData(
    query(
      docRef.parent,
      where(documentId(), "==", docRef.id),
      where(documentId(), "!=", "93OE3unhtzLf2buLXF3e" /* fake ID */), // hack from https://stackoverflow.com/q/67556515/7923918
      where("isPrivate", "==", false)
    )
  );
  const doc = docs?.[0];

  return (
    <>
      <p>{JSON.stringify(doc) ?? "loading"}</p>
      <button onClick={() => void setDoc(docRef, { isPrivate: false })}>
        update
      </button>
    </>
  );
};

ishowta avatar Jan 31 '23 20:01 ishowta

I think it's because of the useEffect dependency used in the code: link, which checks if the query has changed or not inside useDocument(query) before calling onSnapshot (I may be wrong though)

To overcome this issue, you can do something like this:

const query = firestoreChange ? <your query/document reference> : undefined/null;
const [doc, loading, error] = useDocumentData(query);

This will change your query depending on the firestore change and reload fetching This assumes you have a way of identifying the change in frontend (eg: user sign sign in status)

Otherwise, you can use onSnapshot from firebase directly, and even make a custom hook

const unsubscribe = onSnapshot(
	documentReference, 
	(documentSnapshot) => {
		console.log(documentSnapshot.data());
		setValue(documentSnapshot.data());
		.... etc.
	})

You can read more about onSnapshot here: link

Hopefully this helps

allynz avatar Jul 16 '23 21:07 allynz