Marking Items Used
Food Tracker (React + Firebase)
Chapter 9 · Marking Items Used
A short chapter, and a satisfying one — this is where Chapter 2's original data-modeling decision finally gets exercised as a real transition, not just a starting shape.
The Transition: Active to Used
Chapter 2 modeled a used item as one with status: "used" and no expiryDate field at all — not a null value, a genuine absence. Marking an active item used has to actually produce that same shape, which means removing a field on an update, not merely creating a new document without it in the first place.
Why Not Just Set expiryDate to null?
Setting it to null would leave the field genuinely present, with a null value — a different shape from the one Chapter 2 designed, and a different shape from a used item that started out used from the very beginning (Chapter 2's own creation example never included the field at all). Using deleteField() keeps both paths to "used" — created used, or transitioned to used — producing the identical document shape, rather than the app quietly having two different ways of representing the same "no expiry" state depending on how an item got there.
Extending Chapter 6's Rules to Cover This Update
Chapter 6 validated shape at create time and left update/delete permissive until real auth exists (Chapter 11). That's still the right call for who can update — but rules can validate the shape of an update independently of who's making it:
This says: if the update's own resulting document has status == 'used', that same resulting document must not contain an expiryDate field at all. It's not an authorization check — anyone can still perform this update, per Chapter 6's own deferred-to-Chapter-11 stance — it's a data invariant, enforced at the database layer regardless of which client code path triggered the update, or whether that code even remembered to call deleteField() correctly.
deleteField() needs a merge, not a replacedeleteField() works inside updateDoc(), and inside setDoc(ref, data, { merge: true }) — but not a plain, non-merge setDoc(), which replaces the entire document outright. "Delete this one field" only means something when the rest of the document is being preserved around it.
status back to "active" is not enough — the item would then be active with no expiryDate at all, since deleteField() already removed it and undoing the status change doesn't bring it back. A genuine undo needs to either have retained the original expiry date somewhere to restore, or explicitly ask the user for a new one.
Where This Course Is Headed
Recipe lookup for items nearing expiry, Firebase Authentication (which finally tightens the update/delete rules this chapter left permissive), deployment, and a capstone.
Hands-On Exercises
Explain why marking an item used calls deleteField() on expiryDate rather than setting it to null, tying the reason back to Chapter 2's own data model.
📄 View solutionExplain what invariant this chapter's own update rule enforces, and how it differs in kind from Chapter 6's create rule — one validates creation shape, the other validates something else. What is that something else?
📄 View solutionExplain what would go wrong if a future "undo" feature simply flipped status back to active without also restoring expiryDate, and why deleteField() is the reason this specific problem exists.
📄 View solutionChapter 9 Quick Reference
deleteField()— removes a field entirely on update, matching Chapter 2's "absence, not null" design- Consistent shape — created-used and transitioned-to-used items end up identical, not two different representations of "no expiry"
- Update-time rules — Security Rules can validate a transition's resulting shape, not just a new document's creation shape
- A data invariant, not an auth check — this chapter's rule enforces "used items never have expiryDate," regardless of who performs the update
deleteField()needs a merge — works inupdateDoc()or a mergedsetDoc(), not a plain replace- Next chapter: Recipe Lookup with TheMealDB