如何在React项目中基于regNo避免Cloud Firestore重复条目?
问题:React + Cloud Firestore 如何用regNo作为主键避免重复条目并提示用户
我编写了一段向Cloud Firestore提交数据的React代码,表单中设置了唯一标识字段regNo,希望将regNo作为主键避免重复条目,当该regNo已存在时向应用用户弹出提示。我尝试通过在提交逻辑中增加regNo是否为null的判断来实现,但未达到预期效果。
原有提交逻辑代码
import Sidebar from './../../components/sidebar/Sidebar'; import Navbar from './../../components/navbar/Navbar'; import "./new.scss" import { useEffect, useState } from 'react'; import DriveFolderUploadIcon from '@mui/icons-material/DriveFolderUpload'; import { addDoc, collection,doc,getDoc,serverTimestamp, setDoc} from "firebase/firestore"; import { db } from '../../firebase'; import { useNavigate, useParams } from 'react-router-dom'; import { isEmpty } from 'lodash'; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; const New = ({ title, btnUpd }) => { const [file, setFile] = useState(""); const navigate = useNavigate(); const findUs = ["Referral Doctor", "Website", "Friends", "Facebook", "Nil"] const statusCheck = ["married", "divorced", "single"] const titled = ["Mr", "Mrs"] const sexes = ["Male", "Female"] const [userInfo, setUserInfo] = useState({ regNo: "", title: "", firstName: "", lastName: "", sex: "", email: "", reffBy: "", address: "", phoneNumber: "", age: "", date: "", birth: "", status: "", state: "", occupation: "", duration: "", finding: "", }); const {userId} = useParams(); let id = userId useEffect(() => { let getUserDoc = async (id) => { let docRef = doc(db, "mrisystems/" + id); let docSnap = await getDoc(docRef); console.log('getUserDoc() received:', docSnap); if (docSnap.exists) { let theData = docSnap.data(); console.log('getUserDoc() setting userInfo:', theData); setUserInfo(theData); } } if (id) { getUserDoc(id); console.log("IdUser", id) } else { console.log('useEffect() -- no ID value'); } }, [id]); const handleInput = (e) => { const {name, value} = e.target; setUserInfo({...userInfo, [name]: value}); } // 新增/更新现有记录逻辑 const handleAdd = async(e) => { e.preventDefault(); if(isEmpty(id)){ await addDoc(collection(db, "mrisystems"), { ...userInfo, timeStamp: serverTimestamp(), }); toast.error("用户已存在"); } else { await setDoc(doc(db, "mrisystems/" + id), { ...userInfo, timeStamp: serverTimestamp(), }); } navigate("/users") } }
我尝试修改后的提交逻辑代码
// 新增/更新现有记录逻辑 const handleAdd = async(e) => { e.preventDefault(); if(isEmpty(id) && userInfo.regNo===null){ await addDoc(collection(db, "mrisystems "), { ...userInfo, timeStamp: serverTimestamp(), }); toast.error("用户已存在"); } else { await setDoc(doc(db, "mrisystems/" + id), { ...userInfo, timeStamp: serverTimestamp(), }); } navigate("/users") }
解决方案
你的核心问题在于没有真正去Firestore查询regNo是否已存在,且本地判断逻辑错误(初始regNo是空字符串而非null)。要实现用regNo作为主键并避免重复,需要以下步骤:
- 放弃
addDoc(自动生成随机ID),改用setDoc指定regNo作为文档ID,利用Firestore同一集合下文档ID唯一的特性保证主键唯一性。 - 提交前先查询Firestore中是否存在ID为
regNo的文档,存在则提示用户,不存在再创建。
修改后的handleAdd函数:
// 新增/更新记录逻辑 const handleAdd = async(e) => { e.preventDefault(); // 先校验regNo是否为空 if (!userInfo.regNo.trim()) { toast.error("请填写注册编号"); return; } if (isEmpty(id)) { // 新增模式:检查regNo是否已存在 const docRef = doc(db, "mrisystems", userInfo.regNo); const docSnap = await getDoc(docRef); if (docSnap.exists()) { toast.error("该注册编号已存在"); return; } // 不存在则创建,用regNo作为文档ID await setDoc(docRef, { ...userInfo, timeStamp: serverTimestamp(), }); toast.success("用户创建成功"); } else { // 更新模式:直接更新对应ID的文档 await setDoc(doc(db, "mrisystems", id), { ...userInfo, timeStamp: serverTimestamp(), }); toast.success("用户更新成功"); } navigate("/users"); }
额外说明
- 用
regNo作为文档ID后,后续查询、更新该用户时,直接通过regNo定位文档即可,无需依赖自动生成的ID。 - 可以给Firestore的
regNo字段添加唯一索引(虽然用文档ID已经天然唯一,但添加索引能增强数据校验)。 - 建议在表单层面增加
regNo的格式校验,避免非法字符作为文档ID。
内容的提问来源于stack exchange,提问作者Deezy
相关产品推荐
相关产品推荐

