実現したいこと
ブックマーク一覧が一つだけ表示されるようにしたい。
発生している問題・分からないこと
Firebase Firestoreに保存されているブックマーク済みの投稿データを取得し、そのデータをまとめて表示するために書いたコードです。以下の画像参照していただければ分かる通り、同じ内容のブックマーク一覧が何回も繰り返し表示されるようになってしまいました。
該当のソースコード
"use client"; import { auth } from "@/firebase/firebase"; import { db } from "@/firebase/firebase"; import { collection, query, where, getDocs } from "firebase/firestore"; import { useState, useEffect } from "react"; import MusicCard from "@/components/MusicCard"; export default function BookmarksPage() { const [bookmarks, setBookmarks] = useState([]); const [userId, setUserId] = useState(null); useEffect(() => { // ユーザーの認証状態の変更を監視 const unsubscribe = auth.onAuthStateChanged((user) => { if (user) { console.log("ユーザーがログインしています", user.uid); setUserId(user.uid); // ユーザーがログインしたらユーザーIDを保存 } else { console.log("ユーザーがログアウトしています"); setUserId(null); // ログアウトしたらユーザーIDをnullに } }); // クリーンアップ return () => unsubscribe(); }, []); // 初回のみ実行 useEffect(() => { // ユーザーIDがセットされたときにブックマークを取得 if (userId) { const fetchBookmarks = async () => { try { const bookmarksQuery = query( collection(db, "bookmarks"), where("userId", "==", userId) ); const bookmarkSnapshot = await getDocs(bookmarksQuery); const fetchedBookmarks = bookmarkSnapshot.docs.map(doc => ({ ...doc.data(), id: doc.id })); console.log("取得したブックマーク:", fetchedBookmarks); setBookmarks(fetchedBookmarks); // 取得したブックマークをステートにセット } catch (error) { console.error("ブックマークの取得に失敗しました:", error); } }; fetchBookmarks(); } }, [userId]); // userIdが変わるたびに実行 return ( <div className=" flex flex-col items-center justify-center min-h-screen bg-cover bg-center" style={{ backgroundImage: 'url("/images/white_00115.jpg")' }} // 背景画像を設定 > {bookmarks.length > 0 ? ( bookmarks.map((bookmark) => ( <MusicCard key={bookmark.id} music={bookmark} /> )) ) : ( <p className="text-4xl font-bold items-center justify-center mb-35 text-black"> ブックマークされた投稿が表示されるまでしばらくお待ちください…… </p> )} </div> ); }
import { useState, useEffect } from "react"; import { db } from "@/firebase/firebase"; import { collection, query, orderBy, onSnapshot } from "firebase/firestore"; import { useRouter } from "next/navigation"; import { doc, deleteDoc } from "firebase/firestore"; export default function BookmarksPage() { const [bookmarkedPosts, setBookmarkedPosts] = useState([]); const router = useRouter(); useEffect(() => { const q = query(collection(db, "bookmarks"), orderBy("createdAt", "desc")); const unsubscribe = onSnapshot(q, (snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { setBookmarkedPosts((prev) => [...prev, { ...change.doc.data(), id: change.doc.id }]); } if (change.type === "modified") { setBookmarkedPosts((prev) => prev.map((post) => post.id === change.doc.id ? { ...change.doc.data(), id: change.doc.id } : post ) ); } if (change.type === "removed") { setBookmarkedPosts((prev) => prev.filter((post) => post.id !== change.doc.id)); } }); }); return () => unsubscribe(); }, []); const handleDeleteBookmark = async (bookmarkId) => { try { const bookmarkDocRef = doc(db, "bookmarks", bookmarkId); await deleteDoc(bookmarkDocRef); alert("ブックマークを削除しました!"); } catch (error) { console.error("削除エラー:", error.message); } }; return ( <div className="p-4 space-y-4 min-h-screen"> <h2 className="text-3xl font-bold text-black"> ブックマーク一覧</h2> <div className="space-y-3"> {bookmarkedPosts.map((post) => ( <div key={post.id} className="flex flex-col w-370 border p-4 rounded-md bg-white"> <h4>{post.title}</h4> <p>{post.comment}</p> <p>{post.artist?.name || "アーティスト未選択"}</p> {post.artist?.imageUrl && ( <img src={post.artist.imageUrl} alt={post.artist.name} className="w-20 h-20 rounded-full mt-2" /> )} <a href={post.url} target="_blank" rel="noopener noreferrer" className="text-blue-500"> {post.url} </a> <div className="mt-2"> <button onClick={() => handleDeleteBookmark(post.id)} className="ml-340 w-20 bg-red-500 text-white px-4 py-2 rounded" > Delete </button> </div> </div> ))} </div> <button onClick={() => router.push("/")} className="ml-170 bg-blue-500 hover:bg-blue-800 text-white px-4 py-2 rounded mt-4" > ホームに戻る </button> </div> ); }
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
1Firebase onSnapShotがレンダリングの際に多重登録されてるか
2Keyが一意であるか
3useEffectの依存配列を確認
4再レンダリング時に再実行されるないかを確認するためにuseEffectの依存配列が [] になっているか確認(useEffectの中にconsole.logを仕込んで、コンソール上でいつ発火しているかを確認)
など様々な可能性を考えて修正してみましたが一向に解決しませんでした。
補足
ブックマークをすればするほど、同じデータを持ったブックマーク一覧の個数が増えていきます。
画像では3つブックマークしたので、ブックマーク一覧は3つ。4つブックマークしたら、一覧は4つ……と増えていきます。

あなたの回答
tips
プレビュー