0

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?

4
  • 2
    This behavior is expected, since you've kept a state variable in dependency array of a function and inside the function you're trying to update the state variable. Have you tried keeping the dependency array blank so that the function gets fired only at the component mount? i.e, instead of [todos] , write [ ] Commented Jul 19, 2021 at 11:25
  • stackoverflow.com/questions/53715465/… Commented Jul 19, 2021 at 11:26
  • This makes it run only one time, I do not want that, I want it to run everytime a user uses the addTodo function which adds a todo or the setCompletion state function. Commented Jul 20, 2021 at 15:37
  • @AhmadShaker - show more of your code. What it sounds like is that you need to call getTodos multiple times - once when the user is logged in and whenever the todos are updated. Could you show more of your component code - specifically when the todos are updated (setCompletion/addTodo) Commented Jul 20, 2021 at 15:45

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

The user email is not supposed to change, if they are logged in, so I do not think this correct way to do it.
@AhmadShaker Just because the user email is not supposed to change doesn't mean this isn't the correct way to do it. user.email is a dependency of this effect. That is all.
0

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!

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.