React: Why useEffect's default behavior is to run on every render?

Created on 26 Nov 2019  ·  3Comments  ·  Source: facebook/react

Do you want to request a feature or report a bug?
API design question about useEffect

What is the current behavior?
Currently useEffect runs on every render. This default behavior can be dangerous in situations like dealing with HTTP requests when you forget to pass the second argument. This seems to be a common mistake especially for newcomers like myself. I can't think of many (any) patterns where you want to run useEffect on every render. What was the reasoning behind not defaulting to run once?

Question

All 3 comments

By the API design:

useEffect(
    () => {
        // do something
    },
    [/* dependency list */]
);

useEffect will run only when the dependency list changes, and empty array [] means no dependency (i.e. will only run once). It feels logical to me that undefined dependency list means run the function wrapped by useEffect every render.

I can't think of many (any) patterns where you want to run useEffect on every render. What was the reasoning behind not defaulting to run once?

Suppose you are very new to React, and you want to do something whenever a useState variable changes (e.g. POST the new value to a server).

  • With the current behavior, you would POST the same value multiple times, but you would never miss posting a changed value.
  • With the behavior you're suggesting, only the initial value/first change would get POSTed, and further changes would not trigger the useEffect callback.

I think the dependency list is intended to be more of a performance optimization/aid to code simplification.

The current API allows for you to decide between the following behaviors:

  • Run the effect every time. This mimics the old class componentDidMount + componentDidUpdate behavior.
  • Run the effect only once. This mimics the old componentDidMount behavior.
  • Run the effect only when the dependencies change. This is kind of new, although you could (and often did) implement the same thing by component this.props and prevProps in the class component API.

The "default" behavior you describe (when you don't explicitly specify the dependencies) is often the safest since it prevents stale values from being used in closures.

In the future, hopefully we will provide some type of compiler to help automate a lot of this away. In the meantime, we provide an official ESLint plug-in to help make it a little easier. 😄

Was this page helpful?
0 / 5 - 0 ratings