Why is my useEffect Hook executed in an endless loop?

Did you already try the `useEffect` hook, maybe to fetch data on the first mount of your component? How did it work out for you?

I really liked my first experience with the useEffect Hook. Now it’s no longer necessary to refactor a functional component to a class component. At least not just because you need state or some lifecycle methods.

So there it is – our Hook that is going to load some Users from an API to display it in a UserList component:

  useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(result => setData(result.data));
});

Did you too realize that the hook, and – if available – the cleanup function we just wrote are executed endlessly? Just Add a console.log and see!

useEffect is Executed at each Render

Unlike componentDidMount, useEffect is not executed when the component finished mounting, but each time the component is rendered.

That means if you modify the components state inside useEffect, it will cause a re-render of the component, which again causes the execution of useEffect.

That’s not a great thing if you want to load data only once, right?

That’s why useEffect takes an array of dependencies as a second argument.

The Power of Dependencies

There are many use cases of useEffect so this is something critical to know.

Did you know you can pass an array of objects (dependencies) to useEffect? If you do, each time one of those objects change, React will execute this certain Hook.

But what if we only want the hook to execute when the component is mounted and rendered the first time?

In our example case we only want to execute useEffect once when it is rendered the first time. We already know, if we don’t pass an array of dependencies, the Hook will be executed in a loop. But what if our hook does not depend on any other object?

So we pass an empty array.

That’s it:

useEffect(() => {
// this is only executed once
}, [])

If we pass an empty array to useEffect, it’s only executed after the first render.

Wrapping Up

In some cases it makes sense that our Component calls useEffect at each render. But if you want to replace a class component’s componentDidMount with this method, keep in mind to pass either an empty array so you don’t end up with a component that endlessly renders itself.

Improve your React Skills!

Learn about React concepts, helpful libraries or get tips & tricks for deploying your app and many more topics.

I semi regularly post about React. Don't miss out on my future posts! Sign up below to get them delivered directly into your inbox!

9 thoughts on “Why is my useEffect Hook executed in an endless loop?”

  1. This is an anti-pattern to look at useEffect with an empty array as ComponentDidMount. If someone tries to stick props inside this useEffect, intending that it only fires on mount, they are following an anti-pattern described by the React team. Even if you only want someone to render on mount it should always have its dependancies in the array as that is what React expects of it.

    1. Well you’re right, it’s not specifically a replacement for componentDidMount. But if I want something to only fire when the component is mounted, using useEffect with an empty array is currently the only way to achieve this, or did I get that wrong? In all other cases, I agree – if possible, we should provide dependencies. But what if there aren’t any?

      1. if we didn’t add all dependencies, they give some error like below,
        “React Hook useEffect has missing dependencies: ‘XXXXX’, ‘XXXXX’, and ‘XXXX’. Either include them or remove the dependency array”

  2. why I can’t find this piece of advice in React documentation,
    that’s is really frustrating, that React community still keeps documentation as simple as possible for newbie’s only.

    Thank a lot, Andreas.

Leave a Reply

Your email address will not be published. Required fields are marked *