Solved

useEffect runs twice on mount — is my cleanup wrong?

Asked by Rafael Mendes · 608 views · 2 replies

Rafael Mendes8 messages · 1 solution

Empty dependency array, so I expected one run. In development I get two — two fetches, two log lines. In a production build it happens once.

useEffect(() => {
  console.log('mounted');
  fetchThings();
}, []);

Should I be worried, or is this a development thing I can ignore?

2 replies

Daniela RochaSolution

It is Strict Mode, and it is deliberate. In development React mounts the component, unmounts it and mounts it again, specifically so that effects which are not safe to run twice show themselves now rather than in production under a future feature.

So do not silence it — it is telling you the effect has no cleanup. Your fetch is the classic case: two requests race, and the slower one can resolve last and overwrite fresher state. Abort on cleanup:

useEffect(() => {
  const controller = new AbortController();

  fetchThings({ signal: controller.signal })
    .catch((e) => {
      if (e.name !== 'AbortError') throw e;   // a cancelled fetch is not an error
    });

  return () => controller.abort();
}, []);

Once every effect cleans up after itself, the double invocation stops mattering — which is the point of it.

Link
Kenji Morita

Worth adding that it only double-invokes in development. Disabling Strict Mode to make the logs tidy trades a warning you can act on for a bug you will find in production instead.

Link