Back to blogs
Blog
Writing in public
Longer posts — published here first, then cross-posted to Dev.to with a canonical URL back to this site.
Blog
Longer posts — published here first, then cross-posted to Dev.to with a canonical URL back to this site.
Back to blogs
Back to blogs
Postfirestore, reactqueryfireba, react, typescript
In the previous article we set up authentication and now we're ready to add the business and UI...
Date published

slides used in the presentation building the generic component In this example we'll use...

Why You Might Still Pick Next.js Over TanStack Start From a TanStack Start...

A complete theme management system for React applications with SSR support! ✨ 🌟...
In the previous article we set up authentication and now we're ready to add the business and UI logic. link to previous project
first things first we need
1npm i @react-query-firebase/firestore dayjs date-fns2react-day-picker uniqid34npm i -D @types/dayjs @types/uniqid"5
repo link setting up firebase emulator react-query-firebase docs page
tips for this project
>react-query-firebase has many hooks for all kinds of operations >finding the right one is as important as solving the problem
for example
1const query = useFirestoreQuery(["projects"], ref,{2subscribe:true3});45const snapshot = query.data;67return snapshot.docs.map((docSnapshot) => {8 const data = docSnapshot.data();9 return <div key={docSnapshot.id}>{data.name}</div>;10});
this is the hooks for querying a collection with the optional subscribe setting for realtime updates , it's off by default. This hook returns a snapshot which can be full of classes we don't directly need in our project and makes it really hard to manually manage cache on data mutation.
Luckily they have a hook to fetch only the data which was exactly what i needed
1const query = useFirestoreQueryData(["projects"], ref,{2subscribe:true3});45return query.data.map((document) => {6 return <div key={document.id}>{document.name}</div>;7});
like i mentioned earlier react-query does some smart cache management in the backrground to ensure a query isn't ran unless if the data at hand is stale
there's a function invoked on mutate to append the new item to te cache until the next refetch to avoid refetching after every mutation as you'll notice in the code
1 const id = uniqid();2 const ProjectRef = doc(db, "projects", id);3 const mutationProject = useFirestoreDocumentMutation(4 ProjectRef,5 { merge: true },6 {7 onMutate: async (newProject) => {8 // Cancel any outgoing refetches (so they don't overwrite our optimistic update)9 await queryClient.cancelQueries("projects");10 // Snapshot the previous value11 const previousTodos = queryClient.getQueryData("projects");12 // Optimistically update to the new value13 //@ts-ignore14 queryClient.setQueryData("projects", (old) => [...old, newProject]);15 // Return a context object with the snapshotted value16 return { previousTodos };17 },18 // If the mutation fails, use the context returned from onMutate to roll back19 onError: (err, newTodo, context) => {20 //@ts-ignore21 queryClient.setQueryData("projects", context.previousTodos);22 },23 // Always refetch after error or success:24 onSettled: () => {25 queryClient.invalidateQueries("projects");26 },27 }28 );29
you'll also notice that am using uniqid to getrate my own data ids,it's easier when you have to update the data so it's wise to store it as part of te saved document since firebase generates the deafault ones when you mutate using add() server-side and you'll only have access to them when querying . the is is also in the top level of the snapshot response so useFirestoreQueryData will not hve access to it.
Firebase also has a add() and set() methods for data mutation. add() requires a collection reference
`
1import { doc, setDoc } from "firebase/firestore";23await setDoc(doc(db, "cities", "new-city-id"), data);
set() requires a document reference which also requires the doc id, which is what am using since am generating my own ids
1import { doc, setDoc } from "firebase/firestore";23const cityRef = doc(db, 'cities', 'BJ');4setDoc(cityRef, { capital: true }, { merge: true });
another tripping point is date and firestore timestamps
1export interface tyme{2 nanoseconds: number,3 seconds:number4}
so i made a wrapper function to convert them before rendering them to avoid the " objects acnnot be react children error"
1export const toTyme =(time?:tyme)=>{2 if(time){3 const ty= new Date(4 //@ts-ignore5 time.seconds * 1000 + time.nanoseconds / 100000067 );8 return dayjs(ty).format("DD/MM/YYYY")9 }1011 return dayjs(new Date()).format("DD/MM/YYYY")1213}
and that will be it , happy coding