main
js 182 lines 4.52 KB
Raw
1 /**
2 * Exhaustive Deps
3 */
4 // Valid because dependencies are declared correctly
5 function Comment({comment, commentSource}) {
6 const currentUserID = comment.viewer.id;
7 const environment = RelayEnvironment.forUser(currentUserID);
8 const commentID = nullthrows(comment.id);
9 useEffect(() => {
10 const subscription = SubscriptionCounter.subscribeOnce(
11 `StoreSubscription_${commentID}`,
12 () =>
13 StoreSubscription.subscribe(
14 environment,
15 {
16 comment_id: commentID,
17 },
18 currentUserID,
19 commentSource
20 )
21 );
22 return () => subscription.dispose();
23 }, [commentID, commentSource, currentUserID, environment]);
24 }
25
26 // Valid because no dependencies
27 function UseEffectWithNoDependencies() {
28 const local = {};
29 useEffect(() => {
30 console.log(local);
31 });
32 }
33 function UseEffectWithEmptyDependencies() {
34 useEffect(() => {
35 const local = {};
36 console.log(local);
37 }, []);
38 }
39
40 // OK because `props` wasn't defined.
41 function ComponentWithNoPropsDefined() {
42 useEffect(() => {
43 console.log(props.foo);
44 }, []);
45 }
46
47 // Valid because props are declared as a dependency
48 function ComponentWithPropsDeclaredAsDep({foo}) {
49 useEffect(() => {
50 console.log(foo.length);
51 console.log(foo.slice(0));
52 }, [foo]);
53 }
54
55 // Valid because individual props are declared as dependencies
56 function ComponentWithIndividualPropsDeclaredAsDeps(props) {
57 useEffect(() => {
58 console.log(props.foo);
59 console.log(props.bar);
60 }, [props.bar, props.foo]);
61 }
62
63 // Invalid because neither props or props.foo are declared as dependencies
64 function ComponentWithoutDeclaringPropAsDep(props) {
65 useEffect(() => {
66 console.log(props.foo);
67 // eslint-disable-next-line react-hooks/exhaustive-deps
68 }, []);
69 useCallback(() => {
70 console.log(props.foo);
71 // eslint-disable-next-line react-hooks/exhaustive-deps
72 }, []);
73 // eslint-disable-next-line react-hooks/void-use-memo
74 useMemo(() => {
75 console.log(props.foo);
76 // eslint-disable-next-line react-hooks/exhaustive-deps
77 }, []);
78 React.useEffect(() => {
79 console.log(props.foo);
80 // eslint-disable-next-line react-hooks/exhaustive-deps
81 }, []);
82 React.useCallback(() => {
83 console.log(props.foo);
84 // eslint-disable-next-line react-hooks/exhaustive-deps
85 }, []);
86 // eslint-disable-next-line react-hooks/void-use-memo
87 React.useMemo(() => {
88 console.log(props.foo);
89 // eslint-disable-next-line react-hooks/exhaustive-deps
90 }, []);
91 React.notReactiveHook(() => {
92 console.log(props.foo);
93 }, []); // This one isn't a violation
94 }
95
96 /**
97 * Rules of Hooks
98 */
99 // Valid because functions can call functions.
100 function normalFunctionWithConditionalFunction() {
101 if (cond) {
102 doSomething();
103 }
104 }
105
106 // Valid because hooks can call hooks.
107 function useHook() {
108 useState();
109 }
110 const whatever = function useHook() {
111 useState();
112 };
113 const useHook1 = () => {
114 useState();
115 };
116 let useHook2 = () => useState();
117 useHook2 = () => {
118 useState();
119 };
120
121 // Invalid because hooks can't be called in conditionals.
122 function ComponentWithConditionalHook() {
123 if (cond) {
124 // eslint-disable-next-line react-hooks/rules-of-hooks
125 useConditionalHook();
126 }
127 }
128
129 // Invalid because hooks can't be called in loops.
130 function useHookInLoops() {
131 while (a) {
132 // eslint-disable-next-line react-hooks/rules-of-hooks
133 useHook1();
134 if (b) return;
135 // eslint-disable-next-line react-hooks/rules-of-hooks
136 useHook2();
137 }
138 while (c) {
139 // eslint-disable-next-line react-hooks/rules-of-hooks
140 useHook3();
141 if (d) return;
142 // eslint-disable-next-line react-hooks/rules-of-hooks
143 useHook4();
144 }
145 }
146
147 /**
148 * Compiler Rules
149 */
150 // Invalid: component factory
151 function InvalidComponentFactory() {
152 const DynamicComponent = () => <div>Hello</div>;
153 // eslint-disable-next-line react-hooks/static-components
154 return <DynamicComponent />;
155 }
156
157 // Invalid: mutating globals
158 function InvalidGlobals() {
159 // eslint-disable-next-line react-hooks/immutability
160 window.myGlobal = 42;
161 return <div>Done</div>;
162 }
163
164 // Invalid: useMemo with wrong deps
165 function InvalidUseMemo({items}) {
166 // eslint-disable-next-line react-hooks/exhaustive-deps
167 const sorted = useMemo(() => [...items].sort(), []);
168 return <div>{sorted.length}</div>;
169 }
170
171 // Invalid: missing/extra deps in useEffect
172 function InvalidEffectDeps({a, b}) {
173 useEffect(() => {
174 console.log(a);
175 // eslint-disable-next-line react-hooks/exhaustive-deps
176 }, []);
177
178 useEffect(() => {
179 console.log(a);
180 // TODO: eslint-disable-next-line react-hooks/exhaustive-effect-dependencies
181 }, [a, b]);
182 }