'React useEffect infinite loop when using dependencies + cleanup

I know this question gets asked a lot, but from the answers I could find I saw:

  • either no cleanup function in useEffect (so not my use case)
  • people not using dependencies at all (and a simple [] dependency would suffice)

So, here's my code:

import { useEffect, useState } from "react";
import { Button } from "@material-ui/core";
import { CircularProgress } from "@material-ui/core";

export default function App() {
  const [tasks, setTasks] = useState<string[]>([]);

  useEffect(() => {
    const getTasks = () => {
      setTasks(["1", "2", "3"]);
    };
    getTasks();
    return () => {
      setTasks([]);
    };
  }, [tasks]);

  if (tasks.length === 0) {
    return <CircularProgress />;
  }

  return (
    <div>
      {JSON.stringify(tasks)}
      <Button onClick={() => setTasks([])}>Reset tasks data</Button>
    </div>
  );
}

CodeSandbox link: here. I removed the tasks from useEffect dependencies, because otherwise there's an infinite loop and the browser tab may crash.

Let me explain what I want to achieve. This is just a snippet trying to mimic what I'm working on. I have a page with a table. This table is created by pulling some data from an API. So, the page loads, a table gets rendered. Then, under a table I have an "Add task" button. This brings up a little form to the front, where a new task can be created. I pass an onSubmit method to it, so when a user clicks "Create", the POST request is made. And then I want the table to be updated.

To do this, I set the tasks to [], which are state, so component gets re-rendered, useEffect kicks in and re-downloads the tasks, which will contain the newly created one. And it works, but only if I don't use a cleanup function in useEffect. If I do, I enter an infinite loop (because of the tasks dependency I guess).

As I don't want to put axios calls in the example, this is the analogy:

  • getTasks in my app makes the API call
  • the button from the example is the button that opens a form to create a new task
  • didn't want to add a table to the example, so just to show the data, I JSON.stringify() it

Anyway, to the question: I know I need the dependency to make the page refresh when I make the changes (in the example - click the button). How can I achieve what I want (re-render the page after the change to tasks is made) and retain the cleanup function in useEffect? (because if I remove it, everything works)



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source