Firestore集合数据获取时列表重复问题求助
问题描述
我正尝试从Firestore集合中获取文档,但每次在应用中添加文档后,应用内的列表都会出现重复项,而Firestore集合里只能看到该文档的一个ID。
添加数据的代码
import Sidebar from './../../components/sidebar/Sidebar'; import Navbar from './../../components/navbar/Navbar'; import "./new.scss" import { useState } from 'react'; import DriveFolderUploadIcon from '@mui/icons-material/DriveFolderUpload'; import { addDoc, collection, serverTimestamp } from "firebase/firestore"; import { db } from '../../firebase'; import { useNavigate } from 'react-router-dom'; const New = ({inputs}) => { const [file, setFile] = useState(""); const [data, setData] = useState({}); const navigate = useNavigate(); const handleInput = (e) =>{ const id = e.target.id; const value = e.target.value setData({...data, [id]:value}); } const handleAdd = async(e) => { e.preventDefault(); await addDoc(collection(db, "clients"), { ...data, timeStamp: serverTimestamp(), }); navigate("/users") } return ( <div className='new'> <Sidebar/> <div className="newContainer"> <Navbar/> <div className="top"> <h1>Add New Client</h1> </div> <div className="bottom"> <div className="left"> <img src={file ? URL.createObjectURL(file): "https://icon-library.com/images/no-image-icon/no-image-icon-0.jpg"} alt=''/> </div> <div className="right"> <form onSubmit={handleAdd}> <div className="formInput"> <label htmlFor="file"> Image: <DriveFolderUploadIcon className="icon" /> </label> <input type="file" id="file" onChange={(e) => setFile(e.target.files[0])} style={{ display: "none" }} /> </div> {inputs.map((input) => ( <div className="formInput" key={input.id}> <label>{input.label}</label> <input id={input.id} type={input.type} placeholder={input.placeholder} onChange={handleInput} /> </div> ))} <button type="submit"> Send </button> </form> </div> </div> </div> </div> ) } export default New
获取数据的代码
import { DataGrid } from "@mui/x-data-grid"; import "./datatable.scss"; import { userColumns } from "../../datatablesource"; import VisibilityIcon from '@mui/icons-material/Visibility'; import EditNoteIcon from '@mui/icons-material/EditNote'; import DeleteIcon from '@mui/icons-material/Delete'; import { Link } from "react-router-dom"; import { useEffect, useState } from "react"; import { Tooltip } from "@mui/material"; import { collection, getDocs } from "firebase/firestore"; import { doc, deleteDoc } from "firebase/firestore"; import {db} from "../../firebase" const Datatable = () => { const [data, setData] = useState([]); let list = [] useEffect(() =>{ const fetchData = async () =>{ const querySnapshot = await getDocs(collection(db, "clients")); querySnapshot.forEach((doc) => { list.push({ id: doc.id, ...doc.data()}) }); setData(list); } fetchData(); }, []) const handleDelete = async (id) => { try { await deleteDoc(doc(db, "clients", id)); setData(data.filter((item) => item.id !== id)); } catch (err) { console.log(err); } }; const actionColumn = [ { field: "action", headerName: "Action", width: 150, renderCell:(params)=>{ return ( <div className="cellAction"> <Link> <Tooltip title="View Client" placement="bottom"> <div className="viewButton"><VisibilityIcon/></div> </Tooltip> </Link> <Link> <Tooltip title="Edit Client" placement="bottom"> <div className="editButton"><EditNoteIcon/></div> </Tooltip> </Link> <Link> <Tooltip title="Delete Client" placement="bottom"> <div className="deleteButton" onClick={() => handleDelete(params.row.id)}><DeleteIcon/></div> </Tooltip> </Link> </div> ) }} ] return ( <div className="datatable"> <div className="datatableTitle"> Add New Client <Link to="/users/new" className="link"> Add New </Link> </div> <DataGrid className="datagrid" rows={data} columns={userColumns.concat(actionColumn)} pageSize={9} rowsPerPageOptions={[9]} checkboxSelection /> </div> ) } export default Datatable
解决方案
问题根源在Datatable组件的list变量声明位置:它被定义在useEffect外部,属于组件渲染上下文变量,每次组件重新渲染时不会被重置,而是保留之前的内容。这导致每次执行fetchData时,都会往旧的list里追加新数据,最终出现重复项。
基础修复方案
把list的声明移到fetchData函数内部,确保每次获取数据时都从空数组开始:
useEffect(() =>{ const fetchData = async () =>{ // 每次请求都初始化空数组,避免累积旧数据 let list = [] const querySnapshot = await getDocs(collection(db, "clients")); querySnapshot.forEach((doc) => { list.push({ id: doc.id, ...doc.data()}) }); setData(list); } fetchData(); }, [])
优化方案:实时监听数据变化
使用Firestore的onSnapshot实时监听集合变化,代替单次获取的getDocs,这样列表会自动同步Firestore的新增/修改/删除操作,彻底避免重复问题:
import { collection, onSnapshot } from "firebase/firestore"; // ...其他代码 useEffect(() =>{ // 建立实时监听 const unsubscribe = onSnapshot(collection(db, "clients"), (querySnapshot) => { const list = []; querySnapshot.forEach((doc) => { list.push({ id: doc.id, ...doc.data() }); }); setData(list); }); // 组件卸载时取消监听,避免内存泄漏 return () => unsubscribe(); }, [])
内容的提问来源于stack exchange,提问作者Deezy
相关产品推荐
相关产品推荐

