I’m creating a simple note taking app. The functionality I need is the ability to make a new LABEL, and then inside that label create a TITLE, with TAGS(notes) associated to that title. Right now I have it all working nicely, except when I submit the new label, and/or new title/tags, I need to refresh to see the newly added data. I’ve used tutorials as well as trial and error to get to this point, and I’m a newly lamented bootcamp graduate so please be patient with me 🙂 Any advice is greatly appreciated. How can I make it so when I “createLabel” or “createCard”, the data is instantly rendered on screen instead of having to refresh my page?
Here’s the code:
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, } 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"); --- I think I need to add logic to the useEffect maybe? 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 deleteRef = doc(db, "labels", `${doc.data.id}`) 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;
Advertisement
Answer
There are two ways to retrieve data stored in the Cloud Firestore. What you’re using is getDocs()
method which is a method to get the data once. The second one is onSnapshot()
; this sets a listener to receive data-change events which means Cloud Firestore will send your listener an initial snapshot of any changes to your documents, collections of documents, or the results of queries. See sample code below on how to implement the onSnapshot()
method:
useEffect(() => { const getLabels = async () => { const colRef = query(collection(db, '<collection-name>')) onSnapshot(colRef, (snapshot) => { setLabels(snapshot.docs.map(doc => ({ id: doc.id, data: doc.data() }))) }) } getLabels() }, [])