Search code examples
reactjsfirebasegoogle-cloud-firestorertk-query

RTK Query sometimes fetches old data from Cloud Firestore


I'm currently working on a SPA (React, RTK Query with automated refetch) that uses Cloud Firestore as backend. This is my first time working on a larger project and also using Firebase so I really need some input or advice.

The issue I've run into is that when you're logged into your account on the site and do any type of update on your profile, it'll still show the old data (it disappears as soon as I refresh the site).

I've been trying to find the cause and in Redux devtools the sequence looks like this:

  1. User does a change
  2. RTK Query fires updateUserCollection mutation and is pending
  3. When that's fulfilled, it fires getUserData query (pending)
  4. getUserData is fulfilled, but the old data is still on the site

When I check getUserData in Redux devtools I can see that it has fetched the old data. The odd thing is that this problem isn't consistent. After I've refreshed the site and try to do another update, everything will work fine, but every now and then it'll fetch the old data. This also happens on other places on the site (for example when the Admin tries to add a new product).

I've been trying to see if there's a problem with how I've written the RKT Query code, but I'm starting to think that the issue lies with Firebase. Sometimes it looks like it's a bit slower on updating the documents, and I'm wondering if RTK Query is somehow managing to fetch the data before Firebase has had time to update the documents?

I'm thinking of trying to add some sort of time delay to getUserData, but honestly I don't like the idea of trying to force the app to be slower just to accommodate Firebase. It could also be that it's just failing to fetch the data so it uses the old one instead, but I don't know how to verify that.

Any suggestions or ideas on how this problem could be solved?


(Adding an example code from my slices in case I have done a mistake)

getUserData: builder.query({
  async queryFn(uid) {
    try {
      const userQuery = query(
        collection(db, "users"),
        where("userId", "==", uid)
      );
      const querySnapshot = await getDocs(userQuery);
      const data = querySnapshot?.docs[0].data();

      return { data: data };
    } catch (err) {
      return { error: err };
    }
  },
  providesTags: ["Users", "UserData"],
}),
updateUserCollection: builder.mutation({
  async queryFn(data) {
    try {
      const userQuery = query(
        collection(db, "users"),
        where("userId", "==", data[0].userId)
      );
      const querySnapshot = await getDocs(userQuery);

      querySnapshot.forEach(async (doc) => {
        await updateDoc(doc.ref, {
          userCollection: [...data],
        });
      });

      return { data: "edited" };
    } catch (err) {
      return { error: err };
    }
  },
  invalidatesTags: ["Users", "UserData"],
}),

Solution

  • Writing an answer here in case someone else ever has the same issue.

    It seems rewriting some of my code in the Get query and Update mutation did the trick. (I also updated RTK Query from 1.8.5 to 1.9.0 before changing the code but didn't notice a difference).

    Here's how the code looks now after the changes:

    getUserData: builder.query({
      async queryFn(uid) {
        try {
          const docRef = doc(db, "users", uid);
          const docSnap = await getDoc(docRef);
          const data = docSnap.data();
    
          return { data: data };
        } catch (err) {
          return { error: err };
        }
      },
      providesTags: ["UserData"],
    }),
    updateUserCollection: builder.mutation({
      async queryFn(data) {
        try {
          const docRef = doc(db, "users", data[0].userId);
    
          await updateDoc(docRef, {
            userCollection: [...data],
          });
    
          return { data: "updated" };
        } catch (err) {
          return { error: err };
        }
      },
      invalidatesTags: ["UserData"],
    }),