0

I'm at a cross-roads in my Firebase project and needs some general guidance. Here are the facts as I've understood them from research:

  • Firestore queries have a handy feature for pagination (startAt / startAfter)
  • startAt can take a returned record for simplicity, rather than manually passing property values according to the orderBy of your query. I would prefer to use that (hence this question on StackOverflow).
  • Redux toolkit has a best practice that non-serializable objects are not allowed in state.
  • QueryDocumentSnapshot (returned from Firestore queries) objects are non-serializable, but calling .data() produces a serializable JS object.

So this puts me in a difficult place, because I need to store something serializable in state to make RTK happy. But I need to pass the non-serializable QueryDocumentSnapshot for my Firestore query in order for it to actually return results. (passing the result of .data() does not work in my testing).

I am wondering if a lot of people don't use Redux w/ Firestore and therefore are not encountering this problem, because the vanilla React useState hook doesn't complain about whether your value is serializable or not.

Does anyone have advice? I can resort to just passing values directly to startAt, but I would prefer the convenience of passing the originally returned object.

Some pseudo sample code, lifted from several files in my code base:

// gets constructed dynamically, but for illustration, some sample pipeline entries
const pipeline = [
  where('... etc'),
  orderBy('fieldABC'),
  limit(20),
];
const collRef = collection(db, collectionName);
const baseQuery = query(collRef, ...pipeline);

// put in state (1 of 2 ways)
const items = await getDocs(query); // RTK warning about non-serializable
// const items = (await getDocs(query)).map((doc) => doc.data()); // works, but breaks pagination if I pass one of these items to startAt

// later, due to "infinite scroll" want to get another set of records
// if lastItem is a QueryDocumentSnapshot, it works
const lastItem = items[items.length - 1];
const nextPageQuery = query(collRef, ...pipeline, startAt(lastItem);

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Jeff R
  • 459
  • 6
  • 18
  • "I am wondering if a lot of people don't use Redux w/ Firestore" I think you nailed it. Pairing a realtime sync data store with Redux is, in my opinion, extra complexity for no benefit. – Evan Davis Feb 09 '23 at 16:34
  • Thanks for your input @EvanDavis. The draw to RTK for me is the tag-driven caching capabilities. Perhaps I'm overlooking an elegant cache solution for Firestore that would allow me to remove RTK from the project. I'll research on that, but I'm still learning about Firebase. – Jeff R Feb 10 '23 at 22:18

1 Answers1

0

One naive solution that is against the "strong recommendations" of the RTK team (but that may be worth it if you don't mess with certain RTK edge cases, such as utilizing persistence) would be to ignore the non-serializable values:

export const store = configureStore({
  reducer: {
    // ...
  },
  middleware: (getDefaultMiddleware) => {
    const ware = getDefaultMiddleware({
      serializableCheck: {
        ignoreState: true,
      },
    });
    return ware.concat(firebaseApi.middleware);
  },
});

This is a good enough hack for my use case, but may not fully address it for others.

Jeff R
  • 459
  • 6
  • 18