# react-hooks/exhaustive-deps

> Require the dependency array of a hook to list every value its callback reads

React runs an effect again, or recomputes a memoized value, only when something in the dependency array changes. If the callback reads a prop or a state value that isn't in the array, it keeps the value from an earlier render. The effect doesn't run again when the prop changes, and the screen shows old data.

The rule checks `useEffect`, `useLayoutEffect`, `useMemo`, `useCallback`, and `useImperativeHandle`. It also reports dependencies that do nothing, dependency lists it can't read, and an `async` function passed to an effect. You don't have to list values that never change between renders: a `setState` function, a `dispatch` from `useReducer`, a ref from `useRef`, and anything declared outside the component.

This rule is on by default as a warning if your `package.json` depends on `react`.

Most reports come with a suggestion that rewrites the array. `--fix` and `--fix-suggestions` don't apply it, because a new dependency changes when the effect runs. `--fix-dangerously` does.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

On by default as a warning. To turn it off:

```toml bunfig.toml
[lint.rules]
"react-hooks/exhaustive-deps" = "off" # or "error"
```

Every option, set to its default. The ones that are commented out are unset by default.

```toml bunfig.toml
[lint.rules]
"react-hooks/exhaustive-deps" = ["warn", {
  # additionalHooks = <string>,
  requireExplicitEffectDeps = false,
  enableDangerousAutofixThisMayCauseInfiniteLoops = false,
  experimental_autoDependenciesHooks = [], # list of strings
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad */}
```jsx
function Profile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    loadUser(userId).then(setUser);
  }, []); // keeps showing the first user when userId changes

  return <h1>{user?.name}</h1>;
}
```

{/* bad */}
```jsx
function Profile({ userId }) {
  useEffect(async () => {
    await track(userId);
  }, [userId]);
}
```

Not reported:

{/* good */}
```jsx
function Profile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    loadUser(userId).then(setUser);
  }, [userId]);

  return <h1>{user?.name}</h1>;
}
```

{/* good */}
```jsx
function Profile({ userId }) {
  useEffect(() => {
    async function send() {
      await track(userId);
    }
    send();
  }, [userId]);
}
```

An effect without an array runs after every render, so nothing can be missing:

{/* good */}
```jsx
function Title({ text }) {
  useEffect(() => {
    document.title = text;
  });
}
```

## Options

| Option | Default | What it does |
| --- | --- | --- |
| `additionalHooks` | | A regular expression that matches the names of your own hooks that take a callback and a dependency array. |
| `requireExplicitEffectDeps` | `false` | Report an effect that has no second argument. Write `undefined` to say "after every render" on purpose. |
| `enableDangerousAutofixThisMayCauseInfiniteLoops` | `false` | Let `--fix` apply the suggestions. A new dependency can make an effect run in a loop, so read the result. |
| `experimental_autoDependenciesHooks` | `[]` | Names of hooks that get their dependencies from a compiler when you pass `null`. |

{/* bad {"options": [{"additionalHooks": "^useDebouncedEffect$"}]} */}
```jsx
function Search({ query }) {
  useDebouncedEffect(() => {
    search(query);
  }, []);
}
```

{/* bad {"options": [{"requireExplicitEffectDeps": true}]} */}
```jsx
function Title({ text }) {
  useEffect(() => {
    document.title = text;
  });
}
```
