Data Mutation

This page information on how you can mutate data on Firestore, such as updating and deleting, or running transactions and write batches.

All hooks are built upon the useMutation hook, making it super simple to handle loading states, success and errors.

Adding a document to a collection

To add a document to a collection, use the useFirestoreCollectionMutation hook. It accepts a CollectionReference and when the mutation is triggered, the provided data is added as a new document.

import { useFirestoreCollectionMutation } from "@react-query-firebase/firestore";
import { collection } from "firebase/firestore";
import { firestore } from "./firebase";

function AddDocument() {
  const ref = collection(firestore, "products");
  const mutation = useFirestoreCollectionMutation(ref);

  return (
    <>
      <button
        disabled={mutation.isLoading}
        onClick={() => {
          mutation.mutate({
            name: "New product!",
            price: 10,
          });
        }}
      >
        Add document
      </button>
      {mutation.isError && <p>{mutation.error.message}</p>}
    </>
  );
}

Setting / Updating a document

If you need to set or update the data on an existing document, use the useFirestoreDocumentMutation hook.

import { collection, doc } from "firebase/firestore";
import { useFirestoreDocumentMutation } from "@react-query-firebase/firestore";
import { firestore } from "./firebase";

function SetDocument() {
  const collection = collection(firestore, "products");
  const ref = doc(collection, "123");
  const mutation = useFirestoreDocumentMutation(ref);

  return (
    <>
      <button
        disabled={mutation.isLoading}
        onClick={() => {
          mutation.mutate({
            name: "New product!",
            price: 20,
          });
        }}
      >
        Set document
      </button>
      {mutation.isError && <p>{mutation.error.message}</p>}
    </>
  );
}

In this example, the document will be overwritten with the provided data. You can optionally pass SetOptions to indiciate you wish to merge data instead:

const mutation = useFirestoreDocumentMutation(ref, {
  merge: true,
});

Deleting a document

To delete a document from your collection, use the useFirestoreDocumentDeletion hook.

import { collection, doc } from "firebase/firestore";
import { useFirestoreDocumentDeletion } from "@react-query-firebase/firestore";
import { firestore } from "./firebase";

function DeleteDocument() {
  const collection = collection(firestore, "products");
  const ref = doc(collection, "456");
  const mutation = useFirestoreDocumentDeletion(ref);

  return (
    <>
      <button
        disabled={mutation.isLoading}
        onClick={() => {
          mutation.mutate();
        }}
      >
        Delete document
      </button>
      {mutation.isError && <p>{mutation.error.message}</p>}
    </>
  );
}

Running Transactions

To run a transaction, use the useFirestoreTransaction hook. The hook allows you to define the transaction function and execute it when ready.

import { collection, doc } from "firebase/firestore";
import { useFirestoreTransaction } from "@react-query-firebase/firestore";
import { firestore } from "./firebase";

function LikeButton() {
  const collection = collection(firestore, "products");
  const ref = doc(collection, "123");
  const mutation = useFirestoreTransaction(ref, async (tsx) => {
    // Get the document
    const doc = await tsx.get(ref);

    const newLikes = doc.data().likes + 1;

    // Increase the `likes` count
    tsx.update(ref, {
      likes: newLikes,
    });

    return newLikes;
  });

  return (
    <>
      <button
        disabled={mutation.isLoading}
        onClick={() => {
          mutation.mutate();
        }}
      >
        Like!
      </button>
      {mutation.isError && <p>{mutation.error.message}</p>}
    </>
  );
}

To learn more about transactions, read the Firestore guide.

Write Batches

To execute many updates at once, use the useFirestoreWriteBatch hook. Provide the hook with a WriteBatch instance, add batches writes and commit the batch with the mutation trigger.

import { writeBatch, collection, doc } from "firebase/firestore";
import { useFirestoreWriteBatch } from "@react-query-firebase/firestore";
import { firestore } from "./firebase";

function WriteBatchDocuments() {
  // Define the WriteBatch instance
  const batch = writeBatch(firestore);

  const ref = doc(collection(firestore, "products"), "123");

  const mutation = useFirestoreWriteBatch(batch);

  return (
    <>
      {/* Update the WriteBatch instance */}
      <button onClick={() => batch.delete(ref)}>Stage Deletion</button>
      {/* Commit the write batch */}
      <button
        disabled={mutation.isLoading}
        onClick={() => {
          mutation.mutate();
        }}
      >
        Like!
      </button>
      {mutation.isError && <p>{mutation.error.message}</p>}
    </>
  );
}

Side Effects

All mutation hooks are built on-top of the useMutation hook, and allow you define mutation side effects:

const mutation = useFirestoreCollectionMutation(ref, {
  onSuccess() {
    toast.success("Document added!");
  },
  onError(error) {
    toast.error("Failed to add document!");
  },
  onMutate() {
    toast.info("Adding document...");
  },
});

// or on the mutation function!
mutation.mutate(
  { name: "New Product!" },
  {
    onSuccess() {
      toast.success("Document added!");
    },
    onError(error) {
      toast.error("Failed to add document!");
    },
    onMutate() {
      toast.info("Adding document...");
    },
  }
);