Ahmad Shaker
Ahmad Shaker

Reputation: 25

useEffect dependency causing an infinite loop

I am creating a simple agenda application that is hooked up to firebase as a backend. I want to fetch the data on every write and I am trying to dodge an infinite loop.

useEffect(() => {
    const getTodos = () => {
      console.log("I will run");
      db.collection("Users")
        .doc(user.email)
        .collection("Todos")
        .get()
        .then((snapshot) => {
          const loadedTodos = snapshot.docs.map((docs) => {
            return {
              todo: docs.data().todo,
              isCompleted: docs.data().isCompleted,
              id: docs.id,
            };
          });
          setTodos(loadedTodos ?? []);
        });
    };
    getTodos();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [todos]);

The initial todos state is null. However, when I fetch the firebase data, it changes and then causes a rerender on the component itself which causes another fetch again causing an infinite loop. I have broke the limit of the spark plan in firebase because of this and wasted days of work because of temporary blocks. I think this can be fixed with the useCallback hook, but I do not know how. Also, I want to add the same functionality with a chat application (where data will be written from two sides). Is it possible to rerender only when one of the two sides has written or does it have to be an infinite loop in such a case?

Upvotes: 0

Views: 141

Answers (2)

Ahmad Shaker
Ahmad Shaker

Reputation: 25

I just added a new useState hook, shouldComponentRerender, that changes whenever a function of (addTodos, deleteTodo, setCompletionState) is called, and then the shouldComponentRerender state was added as a dependency for the useEffect hook. Gave me the result I wanted. Thanks!

Upvotes: 0

Adam Jenkins
Adam Jenkins

Reputation: 55613

The dependency you want in your effect is user.email

useEffect(() => {
   const getTodos = () => {
     console.log("I will run");
     db.collection("Users")
       .doc(user.email)
       .collection("Todos")
       .get()
       .then((snapshot) => {
         const loadedTodos = snapshot.docs.map((docs) => {
           return {
             todo: docs.data().todo,
             isCompleted: docs.data().isCompleted,
             id: docs.id,
           };
         });
         setTodos(loadedTodos ?? []);
       });
   };
   getTodos();
 }, [user.email]);

You can read this as: every time user.email changes, I will want to refetch the to-do list.

EDIT: The reason you are observing an infinite loop, as others have said, is because your effect's dependency gets changed by the effect itself, which causes the effect to run again. This can often be fixed by "lifting state up" - but in this case, it's because you're using the wrong dependency.

Upvotes: 2

Related Questions