How can I decrement a field value from firestore after submitting a form successfully?



I have these collection of items from firestore:

  • availability : true
  • stocks: 100
  • item: item1

enter image description here

I kind of wanted to decrement the stocks after submitting the form: I have these where() to compare if what the user chose is the same item from the one saved in the firestore.

  function incrementCounter(collref) {
    collref = firestore
      .collection("items")
      .doc()
      .where(selectedItem, "==", selectedItem);

    collref.update({
      stocks: firestore.FieldValue.increment(-1),
    });
  }

This is how I’ll submit my form and I’ve set the incrementCounter() after saving it:

 const handleSubmit = (e) => {
    e.preventDefault();
    try {
      const userRef = firestore.collection("users").doc(id);
      const ref = userRef.set(
        {
         ....
          },
        },

        { merge: true }
      );

      console.log(" saved");
      incrementCounter();
    } catch (err) {
      console.log(err);
    }
  };

There’s no error in submitting the form. However, the incrementCounter() is not working and displays this error:

TypeError: _Firebase_utils__WEBPACK_IMPORTED_MODULE_5__.firestore.collection(...).doc(...).where is not a function

Answer

The where() method exists on a CollectionReference and not a DocumentReference. You also need to get references to those documents first so first get all the matching documents and then update all of them using Promise.all() or Batch Writes:

function incrementCounter() {
  // not param required  ^^
  const collref = firestore
      .collection("items")
      // .doc() <-- remove this
      .where(selectedItem, "==", selectedItem);
      //      ^^^                 ^^^
      //      doc field           field value
      //      "item"              {selectedItemName}

  collRef.get().then(async (qSnap) => {
    const updates = []
    qSnap.docs.forEach((doc) => {
      updates.push(doc.ref.update({ stocks: firebase.firestore.FieldValue.increment(-1) }))
    })
    await Promise.all(updates)
  })
}

If you are updating less than 500 documents, consider using batch writes to make sure all updates either fail or pass:

collRef.get().then(async (qSnap) => {
  const batch = firestore.batch()

  qSnap.docs.forEach((doc) => {
    batch.update(doc.ref, { stocks: firebase.firestore.FieldValue.increment(-1) })
  })

  await batch.commit()
})

You can read more about batch writes in the documentation



Source: stackoverflow