Fred
Fred

Reputation: 212

Infinite Loop with useEffect - ReactJS

I have a problem when using the useEffect hook, it is generating an infinite loop.

I have a list that is loaded as soon as the page is assembled and should also be updated when a new record is found in "developers" state.

See the code:

  const [developers, setDevelopers] = useState<DevelopersData[]>([]);

  const getDevelopers = async () => {
    await api.get('/developers').then(response => {
      setDevelopers(response.data);
    });
  };
  
  // This way, the loop does not happen
  useEffect(() => {
    getDevelopers();
  }, []);

  // This way, infinte loop
  useEffect(() => {
    getDevelopers();
  }, [developers]);

  console.log(developers)

If I remove the developer dependency on the second parameter of useEffect, the loop does not happen, however, the list is not updated when a new record is found. If I insert "developers" in the second parameter of useEffect, the list is updated automatically, however, it goes into an infinite loop.

What am I doing wrong?

complete code (with component): https://gist.github.com/fredarend/c571d2b2fd88c734997a757bac6ab766

Print: enter image description here

Upvotes: 0

Views: 177

Answers (3)

AKX
AKX

Reputation: 169338

The dependencies for useEffect use reference equality, not deep equality. (If you need deep equality comparison for some reason, take a look at use-deep-compare-effect.)

The API call always returns a new array object, so its reference/identity is not the same as it was earlier, triggering useEffect to fire the effect again, etc.

Given that nothing else ever calls setDevelopers, i.e. there's no way for developers to change unless it was from the API call triggered by the effect, there's really no actual need to have developers as a dependency to useEffect; you can just have an empty array as deps: useEffect(() => ..., []). The effect will only be called exactly once.

EDIT: Following the comment clarification,

I register a developer in the form on the left [...] I would like the list to be updated as soon as a new dev is registered.

This is one way to do things:

The idea here is that developers is only ever automatically loaded on component mount. When the user adds a new developer via the AddDeveloperForm, we opportunistically update the local developers state while we're posting the new developer to the backend. Whether or not posting fails, we reload the list from the backend to ensure we have the freshest real state.

const DevList: React.FC = () => {
  const [developers, setDevelopers] = useState<DevelopersData[]>([]);

  const getDevelopers = useCallback(async () => {
    await api.get("/developers").then((response) => {
      setDevelopers(response.data);
    });
  }, [setDevelopers]);

  useEffect(() => {
    getDevelopers();
  }, [getDevelopers]);

  const onAddDeveloper = useCallback(
    async (newDeveloper) => {
      const newDevelopers = developers.concat([newDeveloper]);
      setDevelopers(newDevelopers);
      try {
        await postNewDeveloperToAPI(newDeveloper); // TODO: Implement me
      } catch (e) {
        alert("Oops, failed posting developer information...");
      }
      getDevelopers();
    },
    [developers],
  );

  return (
    <>
      <AddDeveloperForm onAddDeveloper={onAddDeveloper} />
      <DeveloperList developers={developers} />
    </>
  );
};

Upvotes: 2

Manish Juriani
Manish Juriani

Reputation: 48

Use an empty array [] in the second parameter of the useEffect. This causes the code inside to run only on mount of the parent component.

useEffect(() => {
    getDevelopers();
  }, []);

Upvotes: 0

Sam
Sam

Reputation: 2341

The problem is that your getDevelopers function, calls your setDevelopers function, which updates your developers variable. When your developers variable is updated, it triggers the useEffect function

  useEffect(() => {
    getDevelopers();
  }, [developers]);

because developers is one of the dependencies passed to it and the process starts over.

Every time a variable within the array, which is passed as the second argument to useEffect, gets updated, the useEffect function gets triggered

Upvotes: 0

Related Questions