Skip to content

useExhaustiveDependencies (since v1.0.0)

Diagnostic Category: lint/correctness/useExhaustiveDependencies

Sources:

Enforce all dependencies are correctly specified in a React hook.

This rule is a port of the rule react-hooks/exhaustive-deps, and it’s meant to target projects that uses React.

If your project doesn’t use React (or Preact), you shouldn’t use this rule.

The rule will inspect the following known hooks:

  • useEffect
  • useLayoutEffect
  • useInsertionEffect
  • useCallback
  • useMemo
  • useImperativeHandle
  • useState
  • useReducer
  • useRef
  • useDebugValue
  • useDeferredValue
  • useTransition

If you want to add more hooks to the rule, check the options.

import { useEffect } from "react";
function component() {
let a = 1;
useEffect(() => {
console.log(a);
}, []);
}
correctness/useExhaustiveDependencies.js:5:5 lint/correctness/useExhaustiveDependencies ━━━━━━━━━━━━

   This hook does not specify all of its dependencies: a
  
    3 │ function component() {
    4 │     let a = 1;
  > 5 │     useEffect(() => {
       ^^^^^^^^^
    6 │         console.log(a);
    7 │     }, []);
  
   This dependency is not specified in the hook dependency list.
  
    4 │     let a = 1;
    5 │     useEffect(() => {
  > 6 │         console.log(a);
                       ^
    7 │     }, []);
    8 │ }
  
   Either include it or remove the dependency array
  
import { useEffect } from "react";
function component() {
let b = 1;
useEffect(() => {
}, [b]);
}
correctness/useExhaustiveDependencies.js:5:5 lint/correctness/useExhaustiveDependencies ━━━━━━━━━━━━

   This hook specifies more dependencies than necessary: b
  
    3 │ function component() {
    4 │     let b = 1;
  > 5 │     useEffect(() => {
       ^^^^^^^^^
    6 │     }, [b]);
    7 │ }
  
   This dependency can be removed from the list.
  
    4 │     let b = 1;
    5 │     useEffect(() => {
  > 6 │     }, [b]);
           ^
    7 │ }
    8 │ 
  
import { useEffect, useState } from "react";
function component() {
const [name, setName] = useState();
useEffect(() => {
console.log(name);
setName("");
}, [name, setName]);
}
correctness/useExhaustiveDependencies.js:5:5 lint/correctness/useExhaustiveDependencies ━━━━━━━━━━━━

   This hook specifies more dependencies than necessary: setName
  
    3 │ function component() {
    4 │     const [name, setName] = useState();
  > 5 │     useEffect(() => {
       ^^^^^^^^^
    6 │         console.log(name);
    7 │         setName("");
  
   This dependency can be removed from the list.
  
     6 │         console.log(name);
     7 │         setName("");
   > 8 │     }, [name, setName]);
                  ^^^^^^^
     9 │ }
    10 │ 
  
import { useEffect } from "react";
function component() {
let a = 1;
const b = a + 1;
useEffect(() => {
console.log(b);
}, []);
}
correctness/useExhaustiveDependencies.js:6:5 lint/correctness/useExhaustiveDependencies ━━━━━━━━━━━━

   This hook does not specify all of its dependencies: b
  
    4 │     let a = 1;
    5 │     const b = a + 1;
  > 6 │     useEffect(() => {
       ^^^^^^^^^
    7 │         console.log(b);
    8 │     }, []);
  
   This dependency is not specified in the hook dependency list.
  
    5 │     const b = a + 1;
    6 │     useEffect(() => {
  > 7 │         console.log(b);
                       ^
    8 │     }, []);
    9 │ }
  
   Either include it or remove the dependency array
  
import { useEffect } from "react";
function component() {
let a = 1;
useEffect(() => {
console.log(a);
}, [a]);
}
import { useEffect } from "react";
function component() {
const a = 1;
useEffect(() => {
console.log(a);
});
}
import { useEffect, useState } from "react";
function component() {
const [name, setName] = useState();
useEffect(() => {
console.log(name);
setName("");
}, [name]);
}
import { useEffect } from "react";
let outer = false;
function component() {
useEffect(() => {
outer = true;
}, []);
}

Allows to specify custom hooks - from libraries or internal projects - for which dependencies should be checked and/or which are known to have stable return values.

For every hook for which you want the dependencies to be validated, you should specify the index of the closure and the index of the dependencies array to validate against.

{
"//": "...",
"options": {
"hooks": [
{ "name": "useLocation", "closureIndex": 0, "dependenciesIndex": 1},
{ "name": "useQuery", "closureIndex": 1, "dependenciesIndex": 0}
]
}
}

Given the previous example, your hooks can be used like this:

function Foo() {
const location = useLocation(() => {}, []);
const query = useQuery([], () => {});
}

When a hook is known to have a stable return value (its identity doesn’t change across invocations), that value doesn’t need to be specified in dependency arrays. For example, setters returned by React’s useState hook always have the same identity and should be omitted as such.

You can configure custom hooks that return stable results in one of three ways:

  • "stableResult": true — marks the return value as stable. An example of a React hook that would be configured like this is useRef().
  • "stableResult": [1] — expects the return value to be an array and marks the given index or indices to be stable. An example of a React hook that would be configured like this is useState().
  • "stableResult": 1 — shorthand for "stableResult": [1].
{
"//": "...",
"options": {
"hooks": [
{ "name": "useDispatch", "stableResult": true }
]
}
}

With this configuration, the following is valid:

const dispatch = useDispatch();
// No need to list `dispatch` as dependency:
const doAction = useCallback(() => dispatch(someAction()), []);

This rule recognizes rules imported from preact/compat and preact/hooks and applies the same rules as for React hooks.