Search code examples
javascriptreactjsfirebasegoogle-cloud-firestorereactfire

How to enable persistence on reactfire?


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)

Solution

  • 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);