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