3

This useEffect is rendering one time if dependency array is empty but multiple times if i put folderRef in dependency array. I want to render the component only when I add or delete some folder. Please Help

  import React, { useState, useEffect , useRef } from "react";
import { db } from "../firebase";
import { collection, getDocs } from "firebase/firestore";
import FolderData from "./FolderData";

function ShowFolder(props) {
  const [folders, setFolders] = useState([]);
  const folderRef = useRef(collection(db, "folders"));

  useEffect(() => {
    const getData = async () => {
      const data = await getDocs(folderRef.current);
      const folderData = data.docs.map((doc) => {
        return { id: doc.id, data: doc.data() };
      });
      console.log(folderData);
      setFolders(folderData);
    };
    getData();
  }, [folderRef]);

  return (
    <div className="container md:px-4 mx-auto py-10">
      <div className="md:grid lg:grid-cols-6 md:grid-cols-3 mlg:grid-cols-3 md:gap-10 space-y-6 md:space-y-0 px-1 md:px-0 mx-auto">
        {folders.map((folder) => {
          return (
            <div key={folder.id}>
              {folder.data.userId === props.userId && (
                <div>
                  <FolderData key={folder.id} folder={folder} />
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default ShowFolder;
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • It seems that there is a relationship between `setFolders` and `folderRef`. Can you provide more code? Especially the `setFolders` function? And it would be great to have a working example. – René Link Jan 12 '22 at 05:43
  • @RenéLink `setFolders` is likely the state updater function for the `folderData` state. I think the relationship you are referring to would be between the dependency `folderRef` and the `folderData` state since updating the state is what would trigger a rerender. – Drew Reese Jan 12 '22 at 05:48
  • Can you update your question to include a [minimal, complete, and reproducible code example](https://stackoverflow.com/help/minimal-reproducible-example)? It's hard to say what the code we can't see is doing. – Drew Reese Jan 12 '22 at 05:49
  • ` const [folders, setFolders] = useState([]); const folderRef = collection(db, "folders"); useEffect(() => { console.log(folderRef) const getData = async () => { const data = await getDocs(folderRef); const folderData = data.docs.map((doc) => { return { id: doc.id, data: doc.data() }; }); console.log(folderData); setFolders(folderData); }; getData(); }, []); ` this is what i m using – Vaishali Aggarwal Jan 12 '22 at 05:51
  • When you put the `folderRef` in the dependency array of the `useEffect` it means that on every render react checks if the dependency changed. If so the `useEffect` is invoked. Since you update the `folders` state inside the `useEffect` it triggers a rerender. Thus I guess `collection(db, "folders")` does return a different value each or multiple times that causes the `useEffect` to execute and thus another rerender. And so on... – René Link Jan 12 '22 at 06:03
  • i have edited the question. there u can find full code – Vaishali Aggarwal Jan 12 '22 at 06:23

2 Answers2

3

You redeclare folderRef each render cycle, so if you include it in the useEffect hook's dependency array it will trigger render looping.

If you don't refer to folderRef anywhere else in the component then move it into the useEffect hook callback to remove it as an external dependnecy.

const [folders, setFolders] = useState([]);

useEffect(() => {
  const folderRef = collection(db, "folders");

  const getData = async () => {
    const data = await getDocs(folderRef);
    const folderData = data.docs.map((doc) => {
      return { id: doc.id, data: doc.data() };
    });
    console.log(folderData);
    setFolders(folderData);
  };

  getData();
}, []);

Or store it in a React ref so it can be safely referred to as a stable reference.

const [folders, setFolders] = useState([]);
const folderRef = useRef(collection(db, "folders"));

useEffect(() => {
  const getData = async () => {
    const data = await getDocs(folderRef.current);
    const folderData = data.docs.map((doc) => {
      return { id: doc.id, data: doc.data() };
    });
    console.log(folderData);
    setFolders(folderData);
  };

  getData();
}, [folderRef]);

Update

I've gathered that you are updating the folders collection elsewhere in your app and want this component to "listen" for these changes. For this you can implement an onSnapshot listener.

It may look similar to the following:

const [folders, setFolders] = useState([]);

useEffect(() => {
  const unsubscribe = onSnapshot(
    collection(db, "folders"),
    (snapshot) => {
      const folderData = [];
      snapshot.forEach((doc) => {
        folderData.push({
          id: doc.id,
          data: doc.data(),
        });
      });
      setFolders(folderData);
    },
  );

  // Return cleanup function to stop listening to changes
  // on component unmount
  return unsubscribe;
}, []);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • none of it is working. when i add folder the component does not rerender. ` const folderAddHandler = async (e) => { e.preventDefault(); await db.collection("folders").add({ title: folderName, userId: props.userId, }); console.log('added') setFolderName("");` this is my add folder function. – Vaishali Aggarwal Jan 12 '22 at 06:17
  • @VaishaliAggarwal Can you post the complete component code to your question so we can see all that it's trying to do, and when/how it's trying to do it? – Drew Reese Jan 12 '22 at 06:21
  • I have edited the question. you can find full code there – Vaishali Aggarwal Jan 12 '22 at 06:24
  • @VaishaliAggarwal Where do you add a folder? I don't see that `folderAddHandler` function. Are you trying to update a firebase collection and want this component to pick up the changes? – Drew Reese Jan 12 '22 at 06:29
  • yes exactly. The folderAddHandler function is in other component. do i share that too? – Vaishali Aggarwal Jan 12 '22 at 06:31
  • @VaishaliAggarwal For this I believe you want a snapshot listener. You seem to be using the recent version of firebase/firestore (*version 9?*). Would you like me to attempt an example solution for you? – Drew Reese Jan 12 '22 at 06:39
  • 1
    it would be very kind of you if you could do it. I am a newbie in this field and hence dont know much about it. – Vaishali Aggarwal Jan 12 '22 at 06:54
  • @VaishaliAggarwal Sure thing. Please see update section in my answer. I'm not well versed in firebase collections, especially the newest modular system, but I think it's close to what you are after. – Drew Reese Jan 12 '22 at 07:26
0

I think most Probably your useState function is like

const[folderRef , setFolders]=useState(Your Initial Value);

if this is the case then when ever you perform

useEffect(() => {
setFolder(Setting Anything Here)

....


},[folderRef])

React starts an infinity loop coz every time you use setFolder the FolderRef gets updated and the useEffect is forced to run again and it won't Stop .

use something like

const[folderRef , setFolders]=useState(Your Initial Value);
const[isLoading, setIsLoading]=useState(true);


useEffect(() => {
setFolder(Setting Anything Here)
setIsLoading(false)
....


},[])

...


return (
{ isLoading ? "Run the code you wanna run " : null  }
)
Hayder Ali
  • 76
  • 8