Incorrect mental models: - Running code on mount - Running code on some renders but not all

It’s more specific than that.

Primarily, it’s for synchronizing. React handles synchronizing the rendered DOM for a component. But if there’s something else that needs to be updated depending on the state of the component, you do that in an effect. For example, the web page title, or setting up a web socket or interval.

Teardown is for when we are done with the current component props, either because it is being unmounted or because props have changed. If anything needs to be undone, you do that in the cleanup function.

But is it okay to use it for:

  • Calculating values? No, do that in render
  • Loading data from a server? You can use it for that, but React recommends using a library instead of useEffect directly, because there are a lot of edge cases
  • Resetting state? React recommends doing that with a key instead
  • Setting state based on other changed values? Sometimes this is really necessary, and if so, it’s okay to do it in a useEffect


So what’s with the dependency array, and why does it need to be exhaustive?

A reason it’s risky to disable the exhaustive-dependencies checks:

  • It makes the effect work differently than normal, which can violate future developers’ understanding of it
  • It makes the code fragile, because you’re depending on implicit nonstandard configuration about how often it runs, instead of making it explicit
  • It risks breaking in future versions of React, because you’re using it in an undocumented way

The better way is to keep all the dependencies, and then add logic for when some code within the effect should or shouldn’t be run. That is more explicit logic, and is more robust against future changes.