Back to Biomejs

useExhaustiveDependencies

src/content/docs/linter/rules/use-exhaustive-dependencies.mdx

latest42.8 KB
Original Source

import { Tabs, TabItem } from '@astrojs/starlight/components';

<Tabs> <TabItem label="JSX and TSX" icon="seti:javascript"> ## Summary - Rule available since: `v1.0.0` - Diagnostic Category: [`lint/correctness/useExhaustiveDependencies`](/reference/diagnostics#diagnostic-category) - This rule is **recommended**, meaning it is enabled by default. - This rule has an [**unsafe**](/linter/#unsafe-fixes) fix. - The default severity of this rule is [**error**](/reference/diagnostics#error). - This rule belongs to the following domains: - [`react`](/linter/domains#react) - [`next`](/linter/domains#next) - Sources: - Same as [`react-hooks/exhaustive-deps`](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md)

How to configure

json
{
	"linter": {
		"rules": {
			"correctness": {
				"useExhaustiveDependencies": "error"
			}
		}
	}
}

Description

Enforce correct dependency usage within React hooks.

React components have access to various hooks that can perform various actions like querying and updating state.

For hooks that trigger whenever a variable changes (such as useEffect and useMemo), React relies on the hook's listed dependencies array to determine when to re-compute Effects and re-render the page.

This can lead to unexpected behavior when dependencies are incorrectly specified:

jsx

function ticker() {
  const [count, setCount] = useState(0);

  /** Increment the count once per second. */
  function onTick() {
    setCount(count + 1);
  }

  // React _thinks_ this code doesn't depend on anything else, so
  // it will only use the _initial_ version of `onTick` when rendering the component.
  // As a result, our normally-dynamic counter will always display 1!
  // This is referred to as a "stale closure", and is a common pitfall for beginners.
  useEffect(() => {
    const id = setInterval(onTick, 1000);
    return () => clearInterval(id);
  }, []);

  return <h1>Counter: {count}</h1>;
}
jsx
function apples() {
  const [count, setCount] = useState(0);
  const [message, setMessage] = useState("We have 0 apples!");

  // React _thinks_ this code depends on BOTH `count` and `message`, and will re-run the hook whenever
  // `message` is changed despite it not actually being used inside the closure.
  // In fact, this will create an infinite loop due to our hook updating `message` and triggering itself again!
  useEffect(() => {
    setMessage(`We have ${count} apples!`)
  }, [count, message]);

}

This rule attempts to prevent such issues by diagnosing potentially incorrect or invalid usages of hook dependencies.

Default Behavior

By default, the following hooks (and their Preact counterparts) will have their arguments checked by this rule:

  • useEffect
  • useLayoutEffect
  • useInsertionEffect
  • useCallback
  • useMemo
  • useImperativeHandle

Stable results

When a hook is known to have a stable return value (one whose identity doesn't change across invocations), that value doesn't need to and should not be specified as a dependency. For example, setters returned by React's useState hook will not change throughout the lifetime of a program and should therefore be omitted.

By default, the following hooks are considered to have stable return values:

  • useState (index 1)
  • useReducer (index 1)
  • useTransition (index 1)
  • useRef
  • useEffectEvent

If you want to add custom hooks to the rule's diagnostics or specify your own functions with stable results, see the options section for more information.

Examples

Invalid

js
import { useEffect } from "react";

function component() {
  let a = 1;
  useEffect(() => {
    console.log(a);
  }, []);
}
<pre class="language-text"><code class="language-text">code-block.js:5:3 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> <span style="color: #000; background-color: #ddd;"> FIXABLE </span> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook </span><span style="color: Tomato;"><strong>does not specify</strong></span><span style="color: Tomato;"> its dependency on </span><span style="color: Tomato;"><strong>a</strong></span><span style="color: Tomato;">.</span> <strong>3 │ </strong>function component() &#123; <strong>4 │ </strong> let a = 1; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>5 │ </strong> useEffect(() =&gt; &#123; <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>6 │ </strong> console.log(a); <strong>7 │ </strong> &#125;, []); <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This dependency is being used here, but is not specified in the hook dependency list.</span> <strong>4 │ </strong> let a = 1; <strong>5 │ </strong> useEffect(() =&gt; &#123; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>6 │ </strong> console.log(a); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong> <strong>7 │ </strong> &#125;, []); <strong>8 │ </strong>&#125; <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Failing to specify dependencies can result in Effects </span><span style="color: lightgreen;"><strong>not updating correctly</strong></span><span style="color: lightgreen;"> when state changes. </span> <span style="color: lightgreen;">These &quot;stale closures&quot; are a common source of surprising bugs.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Either include it or remove the dependency array.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Unsafe fix</span><span style="color: lightgreen;">: </span><span style="color: lightgreen;">Add the missing dependency </span><span style="color: lightgreen;"><strong>a</strong></span><span style="color: lightgreen;"> to the list.</span> <strong> 7 │ </strong><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span>&#125;,<span style="opacity: 0.8;">·</span>[<span style="color: MediumSeaGreen;">a</span>]); <strong> │ </strong> <span style="color: MediumSeaGreen;">+</span> </code></pre>
js
import { useEffect } from "react";

function badComponent() {
  let a = 1;
  useEffect(() => {
    console.log(a);
  }, "not an array");
}
<pre class="language-text"><code class="language-text">code-block.js:7:6 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This dependencies list is not an array literal.</span> <strong>5 │ </strong> useEffect(() =&gt; &#123; <strong>6 │ </strong> console.log(a); <strong><span style="color: Tomato;">&gt;</span></strong> <strong>7 │ </strong> &#125;, &quot;not an array&quot;); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>8 │ </strong>&#125; <strong>9 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Biome can't statically verify whether you've passed the correct dependencies. </span> <span style="color: lightgreen;">Replace the argument with an array literal and list your dependencies within it.</span> </code></pre>
js
import { useEffect } from "react";

function component() {
    let unused = 1;
    useEffect(() => {}, [unused]);
}
<pre class="language-text"><code class="language-text">code-block.js:5:5 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> <span style="color: #000; background-color: #ddd;"> FIXABLE </span> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook specifies </span><span style="color: Tomato;"><strong>more dependencies than necessary</strong></span><span style="color: Tomato;">: unused.</span> <strong>3 │ </strong>function component() &#123; <strong>4 │ </strong> let unused = 1; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>5 │ </strong> useEffect(() =&gt; &#123;&#125;, [unused]); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>6 │ </strong>&#125; <strong>7 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This dependency can be removed from the list.</span> <strong>3 │ </strong>function component() &#123; <strong>4 │ </strong> let unused = 1; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>5 │ </strong> useEffect(() =&gt; &#123;&#125;, [unused]); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>6 │ </strong>&#125; <strong>7 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Specifying more dependencies than required can lead to </span><span style="color: lightgreen;"><strong>unnecessary re-rendering</strong></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;">and </span><span style="color: lightgreen;"><strong>degraded performance</strong></span><span style="color: lightgreen;">.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Unsafe fix</span><span style="color: lightgreen;">: </span><span style="color: lightgreen;">Remove the extra dependencies from the list.</span> <strong> 5 │ </strong><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span>useEffect(()<span style="opacity: 0.8;">·</span>=&gt;<span style="opacity: 0.8;">·</span>&#123;&#125;,<span style="opacity: 0.8;">·</span>[<span style="color: Tomato;">u</span><span style="color: Tomato;">n</span><span style="color: Tomato;">u</span><span style="color: Tomato;">s</span><span style="color: Tomato;">e</span><span style="color: Tomato;">d</span>]); <strong> │ </strong> <span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span> </code></pre>
js
import { useEffect, useState } from "react";

function component() {
  const [name, setName] = useState();
  useEffect(() => {
    console.log(name);
    setName("i never change and don't need to be here");
  }, [name, setName]);
}
<pre class="language-text"><code class="language-text">code-block.js:5:3 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> <span style="color: #000; background-color: #ddd;"> FIXABLE </span> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook specifies </span><span style="color: Tomato;"><strong>more dependencies than necessary</strong></span><span style="color: Tomato;">: setName.</span> <strong>3 │ </strong>function component() &#123; <strong>4 │ </strong> const [name, setName] = useState(); <strong><span style="color: Tomato;">&gt;</span></strong> <strong>5 │ </strong> useEffect(() =&gt; &#123; <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>6 │ </strong> console.log(name); <strong>7 │ </strong> setName(&quot;i never change and don't need to be here&quot;); <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This dependency can be removed from the list.</span> <strong>6 │ </strong> console.log(name); <strong>7 │ </strong> setName(&quot;i never change and don't need to be here&quot;); <strong><span style="color: Tomato;">&gt;</span></strong> <strong>8 │ </strong> &#125;, [name, setName]); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>9 │ </strong>&#125; <strong>10 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Specifying more dependencies than required can lead to </span><span style="color: lightgreen;"><strong>unnecessary re-rendering</strong></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;">and </span><span style="color: lightgreen;"><strong>degraded performance</strong></span><span style="color: lightgreen;">.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Unsafe fix</span><span style="color: lightgreen;">: </span><span style="color: lightgreen;">Remove the extra dependencies from the list.</span> <strong> 8 │ </strong><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span>&#125;,<span style="opacity: 0.8;">·</span>[name<span style="color: Tomato;">,</span><span style="opacity: 0.8;"><span style="color: Tomato;">·</span></span><span style="color: Tomato;">s</span><span style="color: Tomato;">e</span><span style="color: Tomato;">t</span><span style="color: Tomato;">N</span><span style="color: Tomato;">a</span><span style="color: Tomato;">m</span><span style="color: Tomato;">e</span>]); <strong> │ </strong> <span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span> </code></pre>
js
import { useEffect, useState } from "react";

function component() {
  const name = "foo"
  // name doesn't change, so specifying it is redundant
  useEffect(() => {
    console.log(name);
  }, [name]);
}
<pre class="language-text"><code class="language-text">code-block.js:6:3 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> <span style="color: #000; background-color: #ddd;"> FIXABLE </span> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook specifies </span><span style="color: Tomato;"><strong>more dependencies than necessary</strong></span><span style="color: Tomato;">: name.</span> <strong>4 │ </strong> const name = &quot;foo&quot; <strong>5 │ </strong> // name doesn't change, so specifying it is redundant <strong><span style="color: Tomato;">&gt;</span></strong> <strong>6 │ </strong> useEffect(() =&gt; &#123; <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>7 │ </strong> console.log(name); <strong>8 │ </strong> &#125;, [name]); <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This dependency can be removed from the list.</span> <strong>6 │ </strong> useEffect(() =&gt; &#123; <strong>7 │ </strong> console.log(name); <strong><span style="color: Tomato;">&gt;</span></strong> <strong>8 │ </strong> &#125;, [name]); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>9 │ </strong>&#125; <strong>10 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Specifying more dependencies than required can lead to </span><span style="color: lightgreen;"><strong>unnecessary re-rendering</strong></span><span style="color: lightgreen;"> </span> <span style="color: lightgreen;">and </span><span style="color: lightgreen;"><strong>degraded performance</strong></span><span style="color: lightgreen;">.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Unsafe fix</span><span style="color: lightgreen;">: </span><span style="color: lightgreen;">Remove the extra dependencies from the list.</span> <strong> 8 │ </strong><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span>&#125;,<span style="opacity: 0.8;">·</span>[<span style="color: Tomato;">n</span><span style="color: Tomato;">a</span><span style="color: Tomato;">m</span><span style="color: Tomato;">e</span>]); <strong> │ </strong> <span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span><span style="color: Tomato;">-</span> </code></pre>
js
import { useEffect } from "react";

function component() {
  let a = 1;
  const b = a + 1;
  useEffect(() => {
    console.log(b);
  }, []);
}
<pre class="language-text"><code class="language-text">code-block.js:6:3 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> <span style="color: #000; background-color: #ddd;"> FIXABLE </span> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook </span><span style="color: Tomato;"><strong>does not specify</strong></span><span style="color: Tomato;"> its dependency on </span><span style="color: Tomato;"><strong>b</strong></span><span style="color: Tomato;">.</span> <strong>4 │ </strong> let a = 1; <strong>5 │ </strong> const b = a + 1; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>6 │ </strong> useEffect(() =&gt; &#123; <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>7 │ </strong> console.log(b); <strong>8 │ </strong> &#125;, []); <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This dependency is being used here, but is not specified in the hook dependency list.</span> <strong>5 │ </strong> const b = a + 1; <strong>6 │ </strong> useEffect(() =&gt; &#123; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>7 │ </strong> console.log(b); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong> <strong>8 │ </strong> &#125;, []); <strong>9 │ </strong>&#125; <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Failing to specify dependencies can result in Effects </span><span style="color: lightgreen;"><strong>not updating correctly</strong></span><span style="color: lightgreen;"> when state changes. </span> <span style="color: lightgreen;">These &quot;stale closures&quot; are a common source of surprising bugs.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Either include it or remove the dependency array.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Unsafe fix</span><span style="color: lightgreen;">: </span><span style="color: lightgreen;">Add the missing dependency </span><span style="color: lightgreen;"><strong>b</strong></span><span style="color: lightgreen;"> to the list.</span> <strong> 8 │ </strong><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span>&#125;,<span style="opacity: 0.8;">·</span>[<span style="color: MediumSeaGreen;">b</span>]); <strong> │ </strong> <span style="color: MediumSeaGreen;">+</span> </code></pre>
jsx
import { useCallback } from "react";

function component() {
  const Component = () => null;
  const render = useCallback(() => <Component />, []);
}
<pre class="language-text"><code class="language-text">code-block.jsx:5:18 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> <span style="color: #000; background-color: #ddd;"> FIXABLE </span> ━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook </span><span style="color: Tomato;"><strong>does not specify</strong></span><span style="color: Tomato;"> its dependency on </span><span style="color: Tomato;"><strong>Component</strong></span><span style="color: Tomato;">.</span> <strong>3 │ </strong>function component() &#123; <strong>4 │ </strong> const Component = () =&gt; null; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>5 │ </strong> const render = useCallback(() =&gt; &lt;Component /&gt;, []); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>6 │ </strong>&#125; <strong>7 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This dependency is being used here, but is not specified in the hook dependency list.</span> <strong>3 │ </strong>function component() &#123; <strong>4 │ </strong> const Component = () =&gt; null; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>5 │ </strong> const render = useCallback(() =&gt; &lt;Component /&gt;, []); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>6 │ </strong>&#125; <strong>7 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Failing to specify dependencies can result in Effects </span><span style="color: lightgreen;"><strong>not updating correctly</strong></span><span style="color: lightgreen;"> when state changes. </span> <span style="color: lightgreen;">These &quot;stale closures&quot; are a common source of surprising bugs.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Either include it or remove the dependency array.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Unsafe fix</span><span style="color: lightgreen;">: </span><span style="color: lightgreen;">Add the missing dependency </span><span style="color: lightgreen;"><strong>Component</strong></span><span style="color: lightgreen;"> to the list.</span> <strong> 5 │ </strong><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span>const<span style="opacity: 0.8;">·</span>render<span style="opacity: 0.8;">·</span>=<span style="opacity: 0.8;">·</span>useCallback(()<span style="opacity: 0.8;">·</span>=&gt;<span style="opacity: 0.8;">·</span>&lt;Component<span style="opacity: 0.8;">·</span>/&gt;,<span style="opacity: 0.8;">·</span>[<span style="color: MediumSeaGreen;">C</span><span style="color: MediumSeaGreen;">o</span><span style="color: MediumSeaGreen;">m</span><span style="color: MediumSeaGreen;">p</span><span style="color: MediumSeaGreen;">o</span><span style="color: MediumSeaGreen;">n</span><span style="color: MediumSeaGreen;">e</span><span style="color: MediumSeaGreen;">n</span><span style="color: MediumSeaGreen;">t</span><span style="opacity: 0.8;"><span style="color: MediumSeaGreen;">·</span></span>]); <strong> │ </strong> <span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span> </code></pre>

Valid

js
import { useEffect } from "react";

function component() {
  let a = 1;
  useEffect(() => {
    console.log(a);
  }, [a]);
}
js
import { useEffect } from "react";

function component() {
  const SECONDS_PER_DAY = 60 * 60 * 24;
  useEffect(() => {
    console.log(SECONDS_PER_DAY);
  });
}
js
import { useEffect, useState } from "react";

function component() {
  const [name, setName] = useState();
  useEffect(() => {
    console.log(name);
    setName("");
  }, [name]);
}

Hooks not imported from React are ignored by default (unless specified inside rule options)

ts
import type { EffectCallback, DependencyList } from "react";
// custom useEffect function
declare function useEffect(cb: EffectCallback, deps?: DependencyList): void;

function component() {
  let name = "John Doe";
  useEffect(() => {
    console.log(name);
  }, []);
}

Ignoring a specific dependency

Sometimes you may wish to ignore a diagnostic about a specific dependency without disabling all linting for that hook. To do so, you may specify the name of a specific dependency between parentheses, like this:

js
import { useEffect } from "react";

function component() {
  let a = 1;
  // biome-ignore lint/correctness/useExhaustiveDependencies(a): suppress dependency a
  useEffect(() => {
    console.log(a);
  }, []);
}

If you wish to ignore multiple dependencies, you can add multiple comments and add a reason for each:

js
import { useEffect } from "react";

function component() {
  let a = 1;
  let b = 1;
  // biome-ignore lint/correctness/useExhaustiveDependencies(a): suppress dependency a
  // biome-ignore lint/correctness/useExhaustiveDependencies(b): suppress dependency b
  useEffect(() => {
    console.log(a, b);
  }, []);
}

:::caution Mismatching code & dependencies has a very high risk of creating bugs in your components. By suppressing the linter, you “lie” to React about the values your Effect depends on, so prefer changing the code over suppressing the rule where possible. :::

Options

hooks

Allows specifying custom hooks (from libraries or internal projects) whose dependencies should be checked and/or which are known to have stable return values.

For every hook whose dependencies you want validated, you must specify the index of both the closure using the dependencies and the dependencies array to validate it against.

Example
json
{
	"linter": {
		"rules": {
			"correctness": {
				"useExhaustiveDependencies": {
					"options": {
						"hooks": [
							{
								"name": "useLocation",
								"closureIndex": 0,
								"dependenciesIndex": 1
							},
							{
								"name": "useQuery",
								"closureIndex": 2,
								"dependenciesIndex": 0
							}
						]
					}
				}
			}
		}
	}
}

This would enable checks on the following code snippets:

js
function Foo() {
  let stateVar = 1;
  useLocation(() => {console.log(stateVar)}, []);
}
<pre class="language-text"><code class="language-text">code-block.js:3:3 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> <span style="color: #000; background-color: #ddd;"> FIXABLE </span> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook </span><span style="color: Tomato;"><strong>does not specify</strong></span><span style="color: Tomato;"> its dependency on </span><span style="color: Tomato;"><strong>stateVar</strong></span><span style="color: Tomato;">.</span> <strong>1 │ </strong>function Foo() &#123; <strong>2 │ </strong> let stateVar = 1; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>3 │ </strong> useLocation(() =&gt; &#123;console.log(stateVar)&#125;, []); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>4 │ </strong>&#125; <strong>5 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">This dependency is being used here, but is not specified in the hook dependency list.</span> <strong>1 │ </strong>function Foo() &#123; <strong>2 │ </strong> let stateVar = 1; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>3 │ </strong> useLocation(() =&gt; &#123;console.log(stateVar)&#125;, []); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>4 │ </strong>&#125; <strong>5 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Failing to specify dependencies can result in Effects </span><span style="color: lightgreen;"><strong>not updating correctly</strong></span><span style="color: lightgreen;"> when state changes. </span> <span style="color: lightgreen;">These &quot;stale closures&quot; are a common source of surprising bugs.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Either include it or remove the dependency array.</span> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">Unsafe fix</span><span style="color: lightgreen;">: </span><span style="color: lightgreen;">Add the missing dependency </span><span style="color: lightgreen;"><strong>stateVar</strong></span><span style="color: lightgreen;"> to the list.</span> <strong> 3 │ </strong><span style="opacity: 0.8;">·</span><span style="opacity: 0.8;">·</span>useLocation(()<span style="opacity: 0.8;">·</span>=&gt;<span style="opacity: 0.8;">·</span>&#123;console.log(stateVar)&#125;,<span style="opacity: 0.8;">·</span>[<span style="color: MediumSeaGreen;">s</span><span style="color: MediumSeaGreen;">t</span><span style="color: MediumSeaGreen;">a</span><span style="color: MediumSeaGreen;">t</span><span style="color: MediumSeaGreen;">e</span><span style="color: MediumSeaGreen;">V</span><span style="color: MediumSeaGreen;">a</span><span style="color: MediumSeaGreen;">r</span>]); <strong> │ </strong> <span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span><span style="color: MediumSeaGreen;">+</span> </code></pre>
js
function Foo() {
  let stateVar = 1;
  useQuery([stateVar], "smthng", () => {console.log(stateVar)});
}

Configuring stable results

As previously discussed, the lint rule takes into account so-called stable results and will ensure any such variables are not specified as dependencies.

You can specify custom functions as returning stable results in one of four ways:

  1. "stableResult": true -- marks the return value as stable. An example of a React hook that would be configured like this is useRef().
  2. "stableResult": [1] -- expects the return value to be an array and marks the given indices as stable. An example of a React hook that would be configured like this is useState().
  3. "stableResult": 1 -- shorthand for option 2 ("stableResult": [1]). Useful for hooks that only have a single stable return.
  4. "stableResult": ["setValue"] -- expects the return value to be an object and marks the properties with the given keys as stable.
Example
json
{
	"linter": {
		"rules": {
			"correctness": {
				"useExhaustiveDependencies": {
					"options": {
						"hooks": [
							{
								"name": "useDispatch",
								"stableResult": true
							}
						]
					}
				}
			}
		}
	}
}

With this configuration, the following is valid:

js
const dispatch = useDispatch();
// No need to list `dispatch` as dependency since it doesn't change
const doAction = useCallback(() => dispatch(someAction()), []);

reportUnnecessaryDependencies

If set to false, the rule will not trigger diagnostics for unused dependencies passed to hooks that do not use them.

:::caution Over-specifying dependencies can reduce application performance or even cause infinite loops, so caution is advised. :::

Default: true

Example
json
{
	"linter": {
		"rules": {
			"correctness": {
				"useExhaustiveDependencies": {
					"options": {
						"reportUnnecessaryDependencies": false
					}
				}
			}
		}
	}
}

jsx
import { useEffect } from "react";

function Foo() {
  let stateVar = 1;
  // not used but still OK
  useEffect(() => {}, [stateVar]);
}

reportMissingDependenciesArray

If enabled, the rule will also trigger diagnostics for hooks that lack dependency arrays altogether, requiring any hooks lacking dependencies to explicitly specify an empty array.

Default: false

Example
json
{
	"linter": {
		"rules": {
			"correctness": {
				"useExhaustiveDependencies": {
					"options": {
						"reportMissingDependenciesArray": true
					}
				}
			}
		}
	}
}

jsx
function noArrayYesProblem() {
  let stateVar = 1;
  React.useEffect(() => {});
}
<pre class="language-text"><code class="language-text">code-block.jsx:3:9 <a href="https://biomejs.dev/linter/rules/use-exhaustive-dependencies">lint/correctness/useExhaustiveDependencies</a> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ <strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">This hook does not have a dependencies array.</span> <strong>1 │ </strong>function noArrayYesProblem() &#123; <strong>2 │ </strong> let stateVar = 1; <strong><span style="color: Tomato;">&gt;</span></strong> <strong>3 │ </strong> React.useEffect(() =&gt; &#123;&#125;); <strong> │ </strong> <strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong><strong><span style="color: Tomato;">^</span></strong> <strong>4 │ </strong>&#125; <strong>5 │ </strong> <strong><span style="color: lightgreen;">ℹ</span></strong> <span style="color: lightgreen;">React relies on hook dependencies to determine when to re-compute Effects. </span> <span style="color: lightgreen;">Add an explicit array (i.e. </span><span style="color: lightgreen;"><strong>[]</strong></span><span style="color: lightgreen;">) and list the callback's dependencies inside it.</span> </code></pre> </TabItem> </Tabs>