@@ -26,14 +26,16 @@ use crate::react::hooks::*;
2626use crate :: services:: semantic:: Semantic ;
2727
2828declare_lint_rule ! {
29- /// Enforce all dependencies are correctly specified in a React hook .
29+ /// Enforce correct dependency usage within React hooks .
3030 ///
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.
33- /// However, incorrectly specifying dependencies for a hook can lead to "stale closures" where old versions of state variables
34- /// are used for subsequent page renders.
3533 ///
36- /// This rule attempts to prevent such issues by diagnosing potentially incorrect usages of hook dependencies.
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.
37+ ///
38+ /// This rule attempts to prevent such issues by diagnosing potentially incorrect or invalid usages of hook dependencies.
3739 ///
3840 /// By default, the rule will inspect the following built-in React hooks (as well as their Preact counterparts):
3941 ///
@@ -67,14 +69,26 @@ declare_lint_rule! {
6769 /// }, []);
6870 /// }
6971 /// ```
72+ ///
73+ /// ```js,expect_diagnostic
74+ /// import { useEffect } from "react";
75+ ///
76+ /// function badComponent() {
77+ /// let a = 1;
78+ /// useEffect(() => {
79+ /// console.log(a);
80+ /// }, "not an array");
81+ /// }
82+ /// ```
83+ ///
7084 ///
7185 /// ```js,expect_diagnostic
7286 /// import { useEffect } from "react";
7387 ///
7488 /// function component() {
75- /// let b = 1;
89+ /// let unused = 1;
7690 /// useEffect(() => {
77- /// }, [b ]);
91+ /// }, [unused ]);
7892 /// }
7993 /// ```
8094 ///
@@ -85,7 +99,7 @@ declare_lint_rule! {
8599 /// const [name, setName] = useState();
86100 /// useEffect(() => {
87101 /// console.log(name);
88- /// setName("");
102+ /// setName("shouldn't need to be here ");
89103 /// }, [name, setName]);
90104 /// }
91105 /// ```
@@ -121,6 +135,7 @@ declare_lint_rule! {
121135 /// function component() {
122136 /// const a = 1;
123137 /// useEffect(() => {
138+ /// // a is const here, so there is no mutable state to change
124139 /// console.log(a);
125140 /// });
126141 /// }
@@ -199,8 +214,8 @@ declare_lint_rule! {
199214 /// {
200215 /// "options": {
201216 /// "hooks": [
202- /// { "name": "useLocation", "closureIndex": 0, "dependenciesIndex": 1},
203- /// { "name": "useQuery", "closureIndex": 1, "dependenciesIndex": 0}
217+ /// { "name": "useLocation", "closureIndex": 0, "dependenciesIndex": 1 },
218+ /// { "name": "useQuery", "closureIndex": 1, "dependenciesIndex": 0 }
204219 /// ]
205220 /// }
206221 /// }
@@ -251,7 +266,7 @@ declare_lint_rule! {
251266 ///
252267 /// ```js,use_options
253268 /// const dispatch = useDispatch();
254- /// // No need to list `dispatch` as dependency if it doesn't change
269+ /// // No need to list `dispatch` as dependency since it doesn't change
255270 /// const doAction = useCallback(() => dispatch(someAction()), []);
256271 /// ```
257272 ///
@@ -274,7 +289,7 @@ declare_lint_rule! {
274289 /// ```jsx,use_options
275290 /// function Foo() {
276291 /// let stateVar = 1;
277- /// // not used but still OK
292+ /// // not used but still OK due to disabled rule
278293 /// useEffect(() => {}, [stateVar]);
279294 /// }
280295 /// ```
@@ -297,7 +312,7 @@ declare_lint_rule! {
297312 /// ```
298313 ///
299314 /// ```jsx,use_options,expect_diagnostic
300- /// function Foo () {
315+ /// function NoArrayYesProblem () {
301316 /// let stateVar = 1;
302317 /// useEffect(() => {});
303318 /// }
@@ -919,7 +934,7 @@ impl Rule for UseExhaustiveDependencies {
919934 } => Some ( RuleDiagnostic :: new (
920935 rule_category ! ( ) ,
921936 function_name_range,
922- markup ! { "This hook does not have a dependencies array" } ,
937+ markup ! { "This hook does not have a dependencies array. " } ,
923938 ) ) ,
924939 Fix :: NonLiteralDependenciesArray { expr } => Some (
925940 RuleDiagnostic :: new (
@@ -940,7 +955,11 @@ impl Rule for UseExhaustiveDependencies {
940955 let mut diag = RuleDiagnostic :: new (
941956 rule_category ! ( ) ,
942957 function_name_range,
943- markup ! { "This hook does not specify its dependency on " <Emphasis >{ capture_text. as_ref( ) } </Emphasis >"." } ,
958+ markup ! {
959+ "This hook " <Emphasis >"does not specify" </Emphasis >" its dependency on " <Emphasis >{ capture_text. as_ref( ) } </Emphasis >"."
960+ "\n React is unable to capture changes to variables not included inside hook dependencies, resulting in" <Emphasis >"outdated references" </Emphasis >"
961+ "\n that can produce unexpected results."
962+ } ,
944963 ) ;
945964
946965 for range in captures_range {
@@ -1024,7 +1043,7 @@ impl Rule for UseExhaustiveDependencies {
10241043 rule_category ! ( ) ,
10251044 function_name_range,
10261045 markup ! {
1027- "This hook specifies a dependency more specific that its captures: " { dependency_text. as_ref( ) } ""
1046+ "This hook specifies a dependency more specific than its captures: " { dependency_text. as_ref( ) } ""
10281047 } ,
10291048 )
10301049 . detail ( capture_range, "This capture is more generic than..." )
0 commit comments