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
npm i @react-query-firebase/firestore dayjs date-fns
react-day-picker uniqid
npm i -D @types/dayjs @types/uniqid"
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
const query = useFirestoreQuery(["projects"], ref,{
subscribe:true
});
const snapshot = query.data;
return snapshot.docs.map((docSnapshot) => {
const data = docSnapshot.data();
return <div key={docSnapshot.id}>{data.name}</div>;
});
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
const query = useFirestoreQueryData(["projects"], ref,{
subscribe:true
});
return query.data.map((document) => {
return <div key={document.id}>{document.name}</div>;
});
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
const id = uniqid();
const ProjectRef = doc(db, "projects", id);
const mutationProject = useFirestoreDocumentMutation(
ProjectRef,
{ merge: true },
{
onMutate: async (newProject) => {
// Cancel any outgoing refetches (so they don't overwrite our optimistic update)
await queryClient.cancelQueries("projects");
// Snapshot the previous value
const previousTodos = queryClient.getQueryData("projects");
// Optimistically update to the new value
//@ts-ignore
queryClient.setQueryData("projects", (old) => [...old, newProject]);
// Return a context object with the snapshotted value
return { previousTodos };
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (err, newTodo, context) => {
//@ts-ignore
queryClient.setQueryData("projects", context.previousTodos);
},
// Always refetch after error or success:
onSettled: () => {
queryClient.invalidateQueries("projects");
},
}
);
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
import { doc, setDoc } from "firebase/firestore";
await 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
```
import { doc, setDoc } from "firebase/firestore";
const cityRef = doc(db, 'cities', 'BJ');
setDoc(cityRef, { capital: true }, { merge: true });
```
another tripping point is date and firestore timestamps
```
export interface tyme{
nanoseconds: number,
seconds:number
}
```
so i made a wrapper function to convert them before rendering them to avoid the " objects acnnot be react children error"
```
export const toTyme =(time?:tyme)=>{
if(time){
const ty= new Date(
//@ts-ignore
time.seconds * 1000 + time.nanoseconds / 1000000
);
return dayjs(ty).format("DD/MM/YYYY")
}
return dayjs(new Date()).format("DD/MM/YYYY")
}
```
and that will be it ,
happy coding
[firebase mutaion docs](https://firebase.google.com/docs/firestore/manage-data/add-data)
Top comments (0)