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;
6
  • 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. Commented Jan 12, 2022 at 5: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. Commented Jan 12, 2022 at 5:48
  • Can you update your question to include a minimal, complete, and reproducible code example? It's hard to say what the code we can't see is doing. Commented Jan 12, 2022 at 5: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 Commented Jan 12, 2022 at 5: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... Commented Jan 12, 2022 at 6:03

2 Answers 2

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;
}, []);
Sign up to request clarification or add additional context in comments.

8 Comments

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.
@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?
I have edited the question. you can find full code there
@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?
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.
|
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  }
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.