Skip to main content

第08章:Update① 更新する(updateDoc / setDoc merge)✏️🔁

この章のゴールはシンプル! **「ToDoのタイトルを編集して、Firestoreに安全に保存できる」**ようになることです😆🧩 そして同時に、Firestoreでありがちな事故 「うっかり上書きでフィールド消滅💥」 を回避できるようになります。

(ちなみに本日時点で、Web SDK の firebase は npm で 12.9.0 が最新として表示されています)(npm)


0) まず結論:更新はこの3つを使い分ける🧠✨

Update Methods Comparison

Firestoreの更新は、だいたいこの3パターンで勝てます✌️

やりたいこと使うAPIざっくり何が起きる?よくある注意
一部だけ更新(titleだけ変える等)updateDoc()指定フィールドだけ更新✅ドキュメントが無いと失敗しがち😵
まるごと保存(全部入れ直す)setDoc()ドキュメントを上書き(全置換)✍️既存フィールドが消える事故💥
一部だけ保存(存在しなくてもOK)setDoc(..., { merge: true })指定フィールドだけ反映(足し込み)➕ネスト(オブジェクト)更新は要注意⚠️

setDoc Merge Flow

setDoc()デフォルトは上書きで、merge: true部分反映になる、というのが超重要ポイントです🧯(Firebase)


1) 事故る例:「tagsが消えた…😇」を体験で理解する

Overwrite Accident

たとえば todos/{id} がこうだったとします👇

  • title: "牛乳買う"
  • done: false
  • tags: ["買い物", "冷蔵庫"]

ここで「タイトルだけ変えたい!」と思って、うっかり👇をやると…

await setDoc(todoRef, { title: "牛乳と卵買う" });

tags が消えます(上書きだから)💥😇 → こういう “消し事故” を避けるのがこの章の主役です!


2) 🛠️ 手を動かす:タイトル編集を updateDoc() で実装する✍️⚛️

2-1) 更新用の関数を作る(まずはこれだけでOK)🧩

UpdateDoc Safety

// src/lib/todos/updateTodoTitle.ts
import { doc, updateDoc, serverTimestamp } from "firebase/firestore";
import { db } from "../firebase"; // 例:あなたの db 取得に合わせて調整

export async function updateTodoTitle(todoId: string, newTitle: string) {
const title = newTitle.trim();
if (!title) throw new Error("タイトルが空です");

const todoRef = doc(db, "todos", todoId);

// ✅ 一部だけ更新(安全)
await updateDoc(todoRef, {
title,
updatedAt: serverTimestamp(),
});
}

updateDoc() は「このフィールドだけ変える」用です✅ そして、ドキュメントが存在しないと失敗することがあります(エラー例として “たぶん存在してないよ” 的な説明がドキュメントにも出ます)(Firebase)


2-2) 編集UI(最小のフォーム例)📝✨

// src/components/TodoTitleEditor.tsx
import { useState } from "react";
import { updateTodoTitle } from "../lib/todos/updateTodoTitle";

type Props = {
todoId: string;
initialTitle: string;
onSaved?: () => void;
};

export function TodoTitleEditor({ todoId, initialTitle, onSaved }: Props) {
const [title, setTitle] = useState(initialTitle);
const [saving, setSaving] = useState(false);
const [toast, setToast] = useState<string | null>(null);

const showToast = (msg: string) => {
setToast(msg);
window.setTimeout(() => setToast(null), 1600);
};

const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSaving(true);
try {
await updateTodoTitle(todoId, title);
showToast("保存できた!🎉");
onSaved?.();
} catch (err: any) {
showToast(`保存失敗…😵 ${err?.message ?? ""}`);
} finally {
setSaving(false);
}
};

return (
<div>
<form onSubmit={onSubmit} style={{ display: "flex", gap: 8 }}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
disabled={saving}
placeholder="タイトル"
style={{ flex: 1, padding: 8 }}
/>
<button disabled={saving} type="submit" style={{ padding: "8px 12px" }}>
{saving ? "保存中…" : "保存"}
</button>
</form>

{toast && (
<div style={{ marginTop: 10, padding: 10, border: "1px solid #ddd" }}>
{toast}
</div>
)}
</div>
);
}

これで最低限、編集 → 保存 → 成功メッセージ が完成です🎯✨ (UIはTailwind等に置き換えてOK👍)


3) setDoc(..., { merge: true }):存在しなくても更新したい時の技🪄

updateDoc() は「あるものを更新」向き。 一方で、たとえば ユーザー設定みたいに「無ければ作ってOK」な時は setDoc(merge:true) がラクです😆

import { doc, setDoc, serverTimestamp } from "firebase/firestore";
import { db } from "../firebase";

export async function upsertUserPrefs(uid: string, theme: "light" | "dark") {
const ref = doc(db, "users", uid);

// ✅ 無ければ作る / あれば一部だけ更新(足し込み)
await setDoc(
ref,
{ prefs: { theme }, updatedAt: serverTimestamp() },
{ merge: true }
);
}

ただし!ここで注意点⚠️ prefs が “オブジェクト” だと、書き方によっては prefs全体を置き換える感じになりやすいです(ネストは罠になりがち)😵


4) ネスト(オブジェクト)の安全更新:「ドット記法」を覚える🔧🧠

Dot Notation Safety

「オブジェクトの中の1項目だけ変えたい」なら、ドット記法が便利です✨ Firestoreのドキュメントでも、ネスト更新にはドット記法を使う例が出ています(Firebase)

await updateDoc(ref, {
"prefs.theme": "dark",
updatedAt: serverTimestamp(),
});

5) ついでに覚えると強い:フィールド削除 deleteField() 🗑️✨

「このフィールド、無かったことにしたい!」って時もありますよね😆 そんな時は deleteField() が使えます🧹

import { doc, updateDoc, deleteField } from "firebase/firestore";

await updateDoc(doc(db, "todos", todoId), {
tags: deleteField(),
});

公式ドキュメントでも “Delete fields” として deleteField() の例が載っています(Firebase)


6) 🤖 AIで“更新”を賢くする(Firebase AI Logic を絡める)✨

ここからが「AI導入済み」前提の美味しいところです😋 編集したタイトルをそのまま保存するだけじゃなく、

  • 誤字を直す✍️
  • いい感じに短くする📏
  • 読みやすく整える🧼

みたいな加工を 保存前にAIにやらせると、アプリが一気に実用っぽくなります🎯

Firebase AI Logic の Web 例では、firebase/ai を使ってモデルを作る流れが案内されています(Firebase)

6-1) 例:タイトルをAIに整形させてから updateDoc する🪄

AI Text Correction

イメージはこんな感じ👇(超ざっくり)

  • ユーザー入力:" ぎゅうにゅう かう "
  • AI整形:"牛乳を買う"
  • Firestore更新:title = "牛乳を買う"

実装のコツは:

  • AIの出力をそのまま信じずtrim()空チェックをもう一回する🧯
  • “長すぎる” を防ぐため 最大文字数を決める(例:60文字)✂️

7) 🧠 開発をAIで加速:Gemini CLI / Antigravity の使いどころ

  • Gemini CLI はターミナルでAIに作業を頼める系で、ドキュメントでは ReActMCP などの仕組み(ツール連携)に触れています
  • Google Antigravity は “Mission Control” 的なノリで、計画→実装をエージェントで回しやすい導線が用意されています

この章での実用アイデアはこんな感じ👇

  • 「この setDoc、上書き事故起きない?」 をコードレビューしてもらう🔍
  • ✅ Firestoreのエラー文を貼って 原因と直し方を日本語で説明してもらう🧑‍🏫
  • updateDoc に渡すデータを 型崩れしない形に整えてもらう🧱

🧩 ミニ課題(この章のゴールチェック)🎯✨

次の3つを全部満たせたらクリアです🙆‍♂️🎉

  1. タイトルを編集して保存できる✍️
  2. 保存成功時に「保存できた!🎉」が出る(トースト)
  3. updatedAt: serverTimestamp() を毎回更新している⏱️

余裕があれば+1🔥 4. AI整形(整形後タイトル)を保存できる🤖✨


✅ チェック(言えたら勝ち)🧠💯

  • setDoc() はデフォルトで 上書き。フィールドが消える事故が起きる😇(Firebase)
  • 一部更新は updateDoc() が安全✅
  • 「無ければ作る」も含めたいなら setDoc(..., { merge: true }) が便利🪄(Firebase)
  • ネストは ドット記法が安心🔧(Firebase)
  • フィールド削除は deleteField() 🗑️(Firebase)

よくある詰まりポイント集😵‍💫🧯

  • 保存したのに他フィールドが消えたsetDoc() 上書き事故の可能性大💥(merge:trueupdateDoc()へ)

  • updateDoc が失敗する → そのIDのドキュメントが無い可能性🫠(編集画面に来る前に取得できてるか確認)

  • ネストを更新したら、ネスト全体が置き換わった → ドット記法で “中の1個” を指定する🔧(Firebase)


必要なら、この第8章の内容に合わせて 「第7章の一覧画面」に 編集ボタン→編集フォーム表示 まで自然に繋がる形のサンプル構成にも整理して出しますよ😉✨