2

I'd like to implement Firestore offline persistence on my PWA React app using the reactfire library.

const firestore = useFirestore().enablePersistence();

  let documentReference = firestore
    .collection("food")
    .doc("milkshake");

  const { data } = useFirestoreDocData(documentReference);

but running the code i get an error:

FirebaseError: Firestore has already been started and persistence can no longer be enabled. You can only enable persistence before calling any other methods on a Firestore object.

This component is wrapped inside a <Suspense> as mentioned in the documentation

That database read is the only one that i make in the entire app, how can i solve?

Edit.

Using the example that @Ajordat gave, I've imported the preloadFirestore function inside the App component I do get an error:

"Cannot read property 'name' of undefined".

Whereas adapting (because I cannot use hooks inside the fetch function) the example from @DougStevenson: I've imported useFirestore function in the App component (in order to get the Firestore object) to enable persistence, and then importing it (useFirestore) into my component in order to retrieve the data, but now, I get the same error as before,

Firestore has already been started and persistence can no longer be enabled.

Edit 2:

I've tried to enablePersistence without errors, thank guys, this is my approach, let me know if it is the best:

const firestore = useFirestore();

  React.useEffect(() => {
    firestore.enablePersistence();
  }, []);

And in my custom component:

let docRef = useFirestore()
    .collection("food")
    .doc("milkshake");

  let document = useFirestoreDocDataOnce(docRef);
  console.log(document)

But now I do have a problem, when I log the document, the data are not emitted instantly, yeah I know that it is an asynchronous operation, but the component is wrapped inside a <Suspense>, in this way:

<Suspense fallback={<div>Loading</div>}>
  <FoodComponent foodName={"Milkshake"} />
</Suspense>

But I don't see the loading text before the component is actually rendered.

Does the suspense fragment show the fallback component only while is loading the function (useFirestore) and not the actual data?

Well, I've solved, have to destructure the data, doing like that:

let docRef = useFirestore()
    .collection("food")
    .doc("milkshake");

  let { data: document } = useFirestoreDocData(docRef);
  console.log(document)
Davide Vitiello
  • 328
  • 1
  • 2
  • 12

2 Answers2

1

On other JavaScript libraries for Firestore, enablePersistence() returns a promise. That means it will complete some time in the future, with no guarantees how long it will take. If you're executing the query immediately after you call enablePersistence(), without waiting for the returned promise to become fulfilled, then you will see this error message. That's because the query "beats" the persistence layer and effectively executes first.

You will have to figure out how to use that promise to wait until it's OK to make that query with persistence enabled. For example:

seFirestore().enablePersistence()
.then(() => {
  let documentReference = firestore
    .collection("food")
    .doc("milkshake");
  const { data } = useFirestoreDocData(documentReference);
})
.catch(error => {
  console.error("enablePersistence failed", error);
})

Notice how the query will complete only after the persistence is fully enabled.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Yep this makes sense (also in reactfire it returns a promise) , but even trying using .then it doesn’t work, somehow the enablePersistace is called twice – Davide Vitiello Dec 20 '20 at 02:19
  • That sounds like a different problem. – Doug Stevenson Dec 20 '20 at 02:35
  • Hey @DavideVitiello, the answer provided solves the original question. But if you have already attempted this and you encountered an error, please provide further details so we can try to help you out. – Ajordat Dec 21 '20 at 10:46
  • Well, I’ve tried it once with no luck, as soon as possible I will try again with more time. Just a question, I don’t have to use the preloadFirestore function right? – Davide Vitiello Dec 22 '20 at 15:01
  • @DavideVitiello If it doesn't work without, I would attempt to use it as in [this example](https://github.com/FirebaseExtended/reactfire/issues/235#issuecomment-602695851) where they are using it precisely to `enablePersistence`. – Ajordat Dec 22 '20 at 16:17
  • I've update the original post. Well, I've tried right now, using the example you (@Ajordat), I do get an error: "Cannot read property 'name' of undefined", adapting the example from @DougStevenson, I've imported `useFirestore` in the main App component (in order to get the Firestore object) to enable persistence, and then importing it into my component go retrieve the data, but now, I get the same error as before, "Firestore has already been started and persistence can no longer be enabled". – Davide Vitiello Dec 22 '20 at 19:53
1

Thanks for the suggestion guys @DougStevenson and @Ajordat

In app component:


import { useFirestore } from "reactfire"

...

const firestore = useFirestore();

React.useEffect(() => {
  firestore.enablePersistence();
}, []);



In your custom component, where you want to use Firestore:


import { useFirestore, useFirestoreDocData /* or what you want to use */ } from "reactfire"


let docRef = useFirestore()
  .collection("food")
  .doc("milkshake");

let { data: document } = useFirestoreDocData(docRef);

console.log(document);


Davide Vitiello
  • 328
  • 1
  • 2
  • 12