Skip to content

Commit ace5891

Browse files
committed
Updates les docs + added changeset
1 parent 67fd550 commit ace5891

File tree

1 file changed

+50
-39
lines changed

1 file changed

+50
-39
lines changed

crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,53 @@ declare_lint_rule! {
3131
/// React components have access to various [hooks](https://react.dev/reference/react/hooks) that can perform
3232
/// various actions like querying and updating state.
3333
///
34-
/// Notably, React is **unable** to capture changes to variables not included inside a hook's dependency array, resulting in
35-
/// any such references being "locked" to their initial values even if updated later.
36-
/// Such "stale closures" are a common pitfall among React developers.
34+
/// For hooks that trigger whenever a variable changes ( notably `useEffect`, `useMemo`, and `useCallback`),
35+
/// React attempts
3736
///
37+
/// However, React is **unable** to capture changes to variables not included inside a hook's dependency array,
38+
/// which can result in surprising behavior:
39+
/// ```jsx,ignore
40+
///
41+
/// function ticker() {
42+
/// const [count, setCount] = useState(0);
43+
///
44+
/// /** Increment the count once per second. */
45+
/// function onTick() {
46+
/// setCount(count + 1);
47+
/// }
48+
///
49+
/// // React _thinks_ this code doesn't depend on anything else, so
50+
/// // it will only use the initial version of `onTick` when rendering the component,
51+
/// // making our normally-dynamic counter always display 1.
52+
/// useEffect(() => {
53+
/// const id = setInterval(onTick, 1000);
54+
/// return () => clearInterval(id);
55+
/// }, []);
56+
///
57+
/// return <h1>Counter: {count}</h1>;
58+
/// }
59+
/// ```
60+
///
3861
/// This rule attempts to prevent such issues by diagnosing potentially incorrect or invalid usages of hook dependencies.
3962
///
40-
/// By default, the rule will inspect the following built-in React hooks (as well as their Preact counterparts):
63+
/// By default, the following hooks (and their Preact counterparts) are checked by this rule:
4164
///
4265
/// - `useEffect`
4366
/// - `useLayoutEffect`
4467
/// - `useInsertionEffect`
4568
/// - `useCallback`
4669
/// - `useMemo`
4770
/// - `useImperativeHandle`
48-
/// - `useState`
49-
/// - `useReducer`
50-
/// - `useRef`
51-
/// - `useDebugValue`
52-
/// - `useDeferredValue`
53-
/// - `useTransition`
54-
/// - `useEffectEvent`
5571
///
56-
/// If you want to add more hooks to the rule's diagnostics, see the [options](#options) section for more.
72+
/// If you want to add more hooks to the rule's diagnostics, see the [options](#options) section for information.
73+
///
74+
/// ### Stable results
75+
/// When a hook is known to have a stable return value (one whose identity doesn't change across invocations),
76+
/// that value doesn't need to and should not be specified as a dependency.
77+
/// For example, setters returned by React's `useState` hook will not change throughout the lifetime of a program,
78+
/// and should be omitted as such - they will never change tho.
5779
///
80+
///
5881
/// ## Examples
5982
///
6083
/// ### Invalid
@@ -81,14 +104,12 @@ declare_lint_rule! {
81104
/// }
82105
/// ```
83106
///
84-
///
85107
/// ```js,expect_diagnostic
86108
/// import { useEffect } from "react";
87109
///
88110
/// function component() {
89111
/// let unused = 1;
90-
/// useEffect(() => {
91-
/// }, [unused]);
112+
/// useEffect(() => {}, [unused]);
92113
/// }
93114
/// ```
94115
///
@@ -99,7 +120,7 @@ declare_lint_rule! {
99120
/// const [name, setName] = useState();
100121
/// useEffect(() => {
101122
/// console.log(name);
102-
/// setName("shouldn't need to be here");
123+
/// setName("i am a beacon of stability");
103124
/// }, [name, setName]);
104125
/// }
105126
/// ```
@@ -129,14 +150,15 @@ declare_lint_rule! {
129150
/// }
130151
/// ```
131152
///
153+
/// Constants and recognized stable results do not (and should not) be specified as dependencies:
154+
///
132155
/// ```js
133156
/// import { useEffect } from "react";
134157
///
135158
/// function component() {
136-
/// const a = 1;
159+
/// const SECONDS_PER_DAY = 60 * 60 * 24;
137160
/// useEffect(() => {
138-
/// // a is const here, so there is no mutable state to change
139-
/// console.log(a);
161+
/// console.log(SECONDS_PER_DAY);
140162
/// });
141163
/// }
142164
/// ```
@@ -153,16 +175,6 @@ declare_lint_rule! {
153175
/// }
154176
/// ```
155177
///
156-
/// ```js
157-
/// import { useEffect } from "react";
158-
/// let outer = false;
159-
/// function component() {
160-
/// useEffect(() => {
161-
/// outer = true;
162-
/// }, []);
163-
/// }
164-
/// ```
165-
///
166178
/// ## Ignoring a specific dependency
167179
///
168180
/// Sometimes you may wish to ignore a diagnostic about a specific
@@ -199,6 +211,8 @@ declare_lint_rule! {
199211
/// }
200212
/// ```
201213
///
214+
/// > When dependencies don’t match the code, there is a **very high risk** of introducing bugs. By suppressing the linter, you “lie” to React about the values your Effect depends on.
215+
///
202216
/// ## Options
203217
///
204218
/// ### `hooks`
@@ -215,7 +229,7 @@ declare_lint_rule! {
215229
/// "options": {
216230
/// "hooks": [
217231
/// { "name": "useLocation", "closureIndex": 0, "dependenciesIndex": 1 },
218-
/// { "name": "useQuery", "closureIndex": 1, "dependenciesIndex": 0 }
232+
/// { "name": "useQuery", "closureIndex": 2, "dependenciesIndex": 0 }
219233
/// ]
220234
/// }
221235
/// }
@@ -227,19 +241,16 @@ declare_lint_rule! {
227241
/// function Foo() {
228242
/// let stateVar = 1;
229243
/// const location = useLocation(() => {console.log(stateVar)}, []);
230-
/// const query = useQuery([], () => {console.log(stateVar)});
244+
/// const query = useQuery([], "ignored", () => {console.log(stateVar)});
231245
/// }
232246
/// ```
233247
///
234-
/// #### Stable results
235-
///
236-
/// When a hook is known to have a stable return value (its identity doesn't
237-
/// change across invocations), that value doesn't need to be specified in
238-
/// dependency arrays. For example, setters returned by React's `useState`
239-
/// hook always have the same identity and should be omitted as such.
248+
/// #### Configuring stable results
240249
///
241-
/// You can configure custom hooks that return stable results in one of
242-
/// four ways:
250+
/// As previously discussed, the lint rule takes into account so-called ["stable results"](#stable-results)
251+
/// and will ensure any such variables are _not_ specified as dependencies.
252+
///
253+
/// You can configure custom hooks that return stable results in one of four ways:
243254
///
244255
/// 1. `"stableResult": true` -- marks the return value as stable. An example
245256
/// of a React hook that would be configured like this is `useRef()`.

0 commit comments

Comments
 (0)