如何无需刷新页面,实时渲染Firebase中的新增数据
问题描述
我正在开发一款简易笔记应用,需要实现创建新标签(LABEL)、在标签内创建标题(TITLE)并关联标签内容(TAGS)的功能。目前功能能正常运行,但提交新标签或新标题/内容后,必须刷新页面才能看到新增数据。我刚完成训练营学习,是个新手,恳请各位耐心解答:如何调用createLabel或createCard方法后,数据能立即在页面上渲染,无需刷新页面?
我的代码
import "./App.css"; import { useState, useEffect } from "react"; import React from "react"; // Components import Header from "./components/Header/Header"; // Firebase import { db } from "./firebase-config"; import { collection, getDocs, getDoc, setDoc, doc, updateDoc, addDoc, deleteDoc, arrayUnion, onSnapshot // 后续方案会用到这个 } from "firebase/firestore"; // Clipboard import { CopyToClipboard } from "react-copy-to-clipboard"; function App() { const [newLabel, setNewLabel] = useState(""); const [labels, setLabels] = useState([]); const [activeLabels, setActiveLabels] = useState(""); const [name, setName] = useState(""); const [tags, setTags] = useState(""); const labelCollectionRef = collection(db, "labels"); useEffect(() => { const getLabels = async () => { const data = await getDocs(labelCollectionRef); setLabels(data.docs.map((doc) => ({ ...doc.data(), id: doc.id }))); }; getLabels(); }, []); const createLabel = async () => { await setDoc(doc(db, "labels", newLabel), { id: newLabel }); }; const clickHandler = async (title) => { setActiveLabels(title); }; const createCard = async () => { await updateDoc(doc(db, "labels", activeLabels), { info: arrayUnion({ name: name, tags: tags }), }); }; const deleteLabel = async (i) => { await deleteDoc(doc(db, "labels", activeLabels)); }; return ( <div className="App"> <Header /> <main> <input onChange={(e) => { setNewLabel(e.target.value); }} placeholder="Enter Label Name" type="text" ></input> <button onClick={createLabel}>Create Label</button> {labels.map((label, i) => { if (activeLabels == label.id) { return ( <> <div className="tags__section"> <h2 key={i} className="tags__title" onClick={() => clickHandler(label.id)} > Label: {label.id} <button onClick={() => deleteLabel(i)} className="tags__delete" > DELETE </button> </h2> </div> {label.info && label.info.map((info, i) => { return ( <div key={i}> <h1 className="tags__name">Name: {info.name}</h1> <h2 className="tags__tags" type="text"> Tags: {info.tags} </h2> <CopyToClipboard text={info.tags}> <button className="tags__clipboard">COPY</button> </CopyToClipboard> </div> ); })} <div className="tags__add"> <input onChange={(e) => setName(e.target.value)} type="text" name="title" placeholder="Add title..." ></input> <textarea onChange={(e) => setTags(e.target.value)} name="tags" type="text" placeholder="Add tags..." ></textarea> <button onClick={createCard}>Create Card</button> </div> </> ); } else { return ( <h2 className="tags__title" onClick={() => clickHandler(label.id)} key={i} > Label: {label.id} </h2> ); } })} </main> </div> ); } export default App;
解决方案
有两种常用方法可以实现数据即时渲染,适合不同场景:
方法一:手动更新本地状态(简单直接,适合小项目)
在完成Firestore操作后,直接修改React的labels状态,不用重新拉取整个列表:
1. 修改createLabel函数
const createLabel = async () => { if (!newLabel.trim()) return; // 避免创建空标签 await setDoc(doc(db, "labels", newLabel), { id: newLabel }); // 把新标签添加到本地状态 setLabels(prevLabels => [...prevLabels, { id: newLabel }]); setNewLabel(""); // 清空输入框 };
2. 修改createCard函数
const createCard = async () => { if (!name.trim() || !tags.trim() || !activeLabels) return; // 校验输入 await updateDoc(doc(db, "labels", activeLabels), { info: arrayUnion({ name: name, tags: tags }), }); // 更新当前激活标签的info数组 setLabels(prevLabels => prevLabels.map(label => { if (label.id === activeLabels) { return { ...label, info: label.info ? [...label.info, { name, tags }] : [{ name, tags }] }; } return label; })); setName(""); // 清空输入 setTags(""); };
3. 修改deleteLabel函数
const deleteLabel = async () => { if (!activeLabels) return; await deleteDoc(doc(db, "labels", activeLabels)); // 从本地状态移除删除的标签 setLabels(prevLabels => prevLabels.filter(label => label.id !== activeLabels)); setActiveLabels(""); // 重置激活状态 };
方法二:使用Firestore实时监听(更可靠,自动同步)
用Firestore的onSnapshot替代getDocs,它会实时监听集合变化,自动更新页面状态,不用手动修改:
修改useEffect代码
useEffect(() => { // 监听labels集合的所有变化 const unsubscribe = onSnapshot(labelCollectionRef, (snapshot) => { const updatedLabels = snapshot.docs.map(doc => ({ ...doc.data(), id: doc.id })); setLabels(updatedLabels); }); // 组件卸载时取消监听,防止内存泄漏 return () => unsubscribe(); }, []);
使用这种方法后,你不需要修改createLabel、createCard、deleteLabel的逻辑,Firestore会自动把最新数据推送到页面,甚至其他设备修改数据也能实时同步。
额外优化建议
- 给输入框添加非空校验,避免无效操作
- 操作完成后清空输入框,提升用户体验
- 可以给按钮添加加载状态,防止重复点击
内容的提问来源于stack exchange,提问作者jcodey
相关产品推荐
相关产品推荐

