Firestore with react-query-firebase
Tags: firestore, reactqueryfireba, react, typescript
Published: 2022-05-21 16:48:31

In the previous article we set up authentication and now we're
ready to add the business and UI logic.
[link to previous project](https://dev.to/tigawanna/authenticating-users-in-firebase-and-managing-auth-state-using-react-query-firebase-3d3o)



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](https://github.com/tigawanna/project-mangaer)
[setting up firebase emulator](https://dev.to/tigawanna/firebase-emulator-16l2)
[react-query-firebase docs page](https://react-query-firebase.invertase.dev/firestore)

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)