@samitouri / QOS-React-2 / commits / 06066c1a5a

Make RulesOfHooks-test more consistent with ExhaustiveDeps-test

Small formatting changes to make the tests consistent. ghstack-source-id: 7013a37f1f349273e35a639dd153ffcfc63595ba Pull Request resolved: https://github.com/facebook/react/pull/25369

Lauren Tan committed Sep 30, 2022 at 12:31 UTC 06066c1a5a372110b12b39cc312268437015a043
1 file changed +574 -486
packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js
+574 -486
@@ -19,6 +19,16 @@ ESLintTester.setDefaultConfig({
19 },
20 });
21
22 +/**
23 + * A string template tag that removes padding from the left side of multi-line strings
24 + * @param {Array} strings array of code strings (only one expected)
25 + */
26 +function normalizeIndent(strings) {
27 + const codeLines = strings[0].split('\n');
28 + const leftPadding = codeLines[1].match(/\s+/)[0];
29 + return codeLines.map(line => line.substr(leftPadding.length)).join('\n');
30 +}
31 +
32 // ***************************************************
33 // For easier local testing, you can add to any case:
34 // {
@@ -31,385 +41,449 @@ ESLintTester.setDefaultConfig({
41
42 const tests = {
43 valid: [
34 - `
35 - // Valid because components can use hooks.
36 - function ComponentWithHook() {
37 - useHook();
38 - }
39 - `,
40 - `
41 - // Valid because components can use hooks.
42 - function createComponentWithHook() {
43 - return function ComponentWithHook() {
44 + {
45 + code: normalizeIndent`
46 + // Valid because components can use hooks.
47 + function ComponentWithHook() {
48 useHook();
45 - };
46 - }
47 - `,
48 - `
49 - // Valid because hooks can use hooks.
50 - function useHookWithHook() {
51 - useHook();
52 - }
53 - `,
54 - `
55 - // Valid because hooks can use hooks.
56 - function createHook() {
57 - return function useHookWithHook() {
49 + }
50 + `,
51 + },
52 + {
53 + code: normalizeIndent`
54 + // Valid because components can use hooks.
55 + function createComponentWithHook() {
56 + return function ComponentWithHook() {
57 + useHook();
58 + };
59 + }
60 + `,
61 + },
62 + {
63 + code: normalizeIndent`
64 + // Valid because hooks can use hooks.
65 + function useHookWithHook() {
66 useHook();
67 }
60 - }
61 - `,
62 - `
63 - // Valid because components can call functions.
64 - function ComponentWithNormalFunction() {
65 - doSomething();
66 - }
67 - `,
68 - `
69 - // Valid because functions can call functions.
70 - function normalFunctionWithNormalFunction() {
71 - doSomething();
72 - }
73 - `,
74 - `
75 - // Valid because functions can call functions.
76 - function normalFunctionWithConditionalFunction() {
77 - if (cond) {
68 + `,
69 + },
70 + {
71 + code: normalizeIndent`
72 + // Valid because hooks can use hooks.
73 + function createHook() {
74 + return function useHookWithHook() {
75 + useHook();
76 + }
77 + }
78 + `,
79 + },
80 + {
81 + code: normalizeIndent`
82 + // Valid because components can call functions.
83 + function ComponentWithNormalFunction() {
84 + doSomething();
85 + }
86 + `,
87 + },
88 + {
89 + code: normalizeIndent`
90 + // Valid because functions can call functions.
91 + function normalFunctionWithNormalFunction() {
92 doSomething();
93 }
80 - }
81 - `,
82 - `
83 - // Valid because functions can call functions.
84 - function functionThatStartsWithUseButIsntAHook() {
85 - if (cond) {
86 - userFetch();
87 - }
88 - }
89 - `,
90 - `
91 - // Valid although unconditional return doesn't make sense and would fail other rules.
92 - // We could make it invalid but it doesn't matter.
93 - function useUnreachable() {
94 - return;
95 - useHook();
96 - }
97 - `,
98 - `
99 - // Valid because hooks can call hooks.
100 - function useHook() { useState(); }
101 - const whatever = function useHook() { useState(); };
102 - const useHook1 = () => { useState(); };
103 - let useHook2 = () => useState();
104 - useHook2 = () => { useState(); };
105 - ({useHook: () => { useState(); }});
106 - ({useHook() { useState(); }});
107 - const {useHook3 = () => { useState(); }} = {};
108 - ({useHook = () => { useState(); }} = {});
109 - Namespace.useHook = () => { useState(); };
110 - `,
111 - `
112 - // Valid because hooks can call hooks.
113 - function useHook() {
114 - useHook1();
115 - useHook2();
116 - }
117 - `,
118 - `
119 - // Valid because hooks can call hooks.
120 - function createHook() {
121 - return function useHook() {
94 + `,
95 + },
96 + {
97 + code: normalizeIndent`
98 + // Valid because functions can call functions.
99 + function normalFunctionWithConditionalFunction() {
100 + if (cond) {
101 + doSomething();
102 + }
103 + }
104 + `,
105 + },
106 + {
107 + code: normalizeIndent`
108 + // Valid because functions can call functions.
109 + function functionThatStartsWithUseButIsntAHook() {
110 + if (cond) {
111 + userFetch();
112 + }
113 + }
114 + `,
115 + },
116 + {
117 + code: normalizeIndent`
118 + // Valid although unconditional return doesn't make sense and would fail other rules.
119 + // We could make it invalid but it doesn't matter.
120 + function useUnreachable() {
121 + return;
122 + useHook();
123 + }
124 + `,
125 + },
126 + {
127 + code: normalizeIndent`
128 + // Valid because hooks can call hooks.
129 + function useHook() { useState(); }
130 + const whatever = function useHook() { useState(); };
131 + const useHook1 = () => { useState(); };
132 + let useHook2 = () => useState();
133 + useHook2 = () => { useState(); };
134 + ({useHook: () => { useState(); }});
135 + ({useHook() { useState(); }});
136 + const {useHook3 = () => { useState(); }} = {};
137 + ({useHook = () => { useState(); }} = {});
138 + Namespace.useHook = () => { useState(); };
139 + `,
140 + },
141 + {
142 + code: normalizeIndent`
143 + // Valid because hooks can call hooks.
144 + function useHook() {
145 useHook1();
146 useHook2();
124 - };
125 - }
126 - `,
127 - `
128 - // Valid because hooks can call hooks.
129 - function useHook() {
130 - useState() && a;
131 - }
132 - `,
133 - `
134 - // Valid because hooks can call hooks.
135 - function useHook() {
136 - return useHook1() + useHook2();
137 - }
138 - `,
139 - `
140 - // Valid because hooks can call hooks.
141 - function useHook() {
142 - return useHook1(useHook2());
143 - }
144 - `,
145 - `
146 - // Valid because hooks can be used in anonymous arrow-function arguments
147 - // to forwardRef.
148 - const FancyButton = React.forwardRef((props, ref) => {
149 - useHook();
150 - return <button {...props} ref={ref} />
151 - });
152 - `,
153 - `
154 - // Valid because hooks can be used in anonymous function arguments to
155 - // forwardRef.
156 - const FancyButton = React.forwardRef(function (props, ref) {
157 - useHook();
158 - return <button {...props} ref={ref} />
159 - });
160 - `,
161 - `
162 - // Valid because hooks can be used in anonymous function arguments to
163 - // forwardRef.
164 - const FancyButton = forwardRef(function (props, ref) {
165 - useHook();
166 - return <button {...props} ref={ref} />
167 - });
168 - `,
169 - `
170 - // Valid because hooks can be used in anonymous function arguments to
171 - // React.memo.
172 - const MemoizedFunction = React.memo(props => {
173 - useHook();
174 - return <button {...props} />
175 - });
176 - `,
177 - `
178 - // Valid because hooks can be used in anonymous function arguments to
179 - // memo.
180 - const MemoizedFunction = memo(function (props) {
181 - useHook();
182 - return <button {...props} />
183 - });
184 - `,
185 - `
186 - // Valid because classes can call functions.
187 - // We don't consider these to be hooks.
188 - class C {
189 - m() {
190 - this.useHook();
191 - super.useHook();
192 - }
193 - }
194 - `,
195 - `
196 - // Valid -- this is a regression test.
197 - jest.useFakeTimers();
198 - beforeEach(() => {
199 - jest.useRealTimers();
200 - })
201 - `,
202 - `
203 - // Valid because they're not matching use[A-Z].
204 - fooState();
205 - use();
206 - _use();
207 - _useState();
208 - use_hook();
209 - // also valid because it's not matching the PascalCase namespace
210 - jest.useFakeTimer()
211 - `,
212 - `
213 - // Regression test for some internal code.
214 - // This shows how the "callback rule" is more relaxed,
215 - // and doesn't kick in unless we're confident we're in
216 - // a component or a hook.
217 - function makeListener(instance) {
218 - each(pixelsWithInferredEvents, pixel => {
219 - if (useExtendedSelector(pixel.id) && extendedButton) {
220 - foo();
147 + }
148 + `,
149 + },
150 + {
151 + code: normalizeIndent`
152 + // Valid because hooks can call hooks.
153 + function createHook() {
154 + return function useHook() {
155 + useHook1();
156 + useHook2();
157 + };
158 + }
159 + `,
160 + },
161 + {
162 + code: normalizeIndent`
163 + // Valid because hooks can call hooks.
164 + function useHook() {
165 + useState() && a;
166 + }
167 + `,
168 + },
169 + {
170 + code: normalizeIndent`
171 + // Valid because hooks can call hooks.
172 + function useHook() {
173 + return useHook1() + useHook2();
174 + }
175 + `,
176 + },
177 + {
178 + code: normalizeIndent`
179 + // Valid because hooks can call hooks.
180 + function useHook() {
181 + return useHook1(useHook2());
182 + }
183 + `,
184 + },
185 + {
186 + code: normalizeIndent`
187 + // Valid because hooks can be used in anonymous arrow-function arguments
188 + // to forwardRef.
189 + const FancyButton = React.forwardRef((props, ref) => {
190 + useHook();
191 + return <button {...props} ref={ref} />
192 + });
193 + `,
194 + },
195 + {
196 + code: normalizeIndent`
197 + // Valid because hooks can be used in anonymous function arguments to
198 + // forwardRef.
199 + const FancyButton = React.forwardRef(function (props, ref) {
200 + useHook();
201 + return <button {...props} ref={ref} />
202 + });
203 + `,
204 + },
205 + {
206 + code: normalizeIndent`
207 + // Valid because hooks can be used in anonymous function arguments to
208 + // forwardRef.
209 + const FancyButton = forwardRef(function (props, ref) {
210 + useHook();
211 + return <button {...props} ref={ref} />
212 + });
213 + `,
214 + },
215 + {
216 + code: normalizeIndent`
217 + // Valid because hooks can be used in anonymous function arguments to
218 + // React.memo.
219 + const MemoizedFunction = React.memo(props => {
220 + useHook();
221 + return <button {...props} />
222 + });
223 + `,
224 + },
225 + {
226 + code: normalizeIndent`
227 + // Valid because hooks can be used in anonymous function arguments to
228 + // memo.
229 + const MemoizedFunction = memo(function (props) {
230 + useHook();
231 + return <button {...props} />
232 + });
233 + `,
234 + },
235 + {
236 + code: normalizeIndent`
237 + // Valid because classes can call functions.
238 + // We don't consider these to be hooks.
239 + class C {
240 + m() {
241 + this.useHook();
242 + super.useHook();
243 + }
244 + }
245 + `,
246 + },
247 + {
248 + code: normalizeIndent`
249 + // Valid -- this is a regression test.
250 + jest.useFakeTimers();
251 + beforeEach(() => {
252 + jest.useRealTimers();
253 + })
254 + `,
255 + },
256 + {
257 + code: normalizeIndent`
258 + // Valid because they're not matching use[A-Z].
259 + fooState();
260 + use();
261 + _use();
262 + _useState();
263 + use_hook();
264 + // also valid because it's not matching the PascalCase namespace
265 + jest.useFakeTimer()
266 + `,
267 + },
268 + {
269 + code: normalizeIndent`
270 + // Regression test for some internal code.
271 + // This shows how the "callback rule" is more relaxed,
272 + // and doesn't kick in unless we're confident we're in
273 + // a component or a hook.
274 + function makeListener(instance) {
275 + each(pixelsWithInferredEvents, pixel => {
276 + if (useExtendedSelector(pixel.id) && extendedButton) {
277 + foo();
278 + }
279 + });
280 + }
281 + `,
282 + },
283 + {
284 + code: normalizeIndent`
285 + // This is valid because "use"-prefixed functions called in
286 + // unnamed function arguments are not assumed to be hooks.
287 + React.unknownFunction((foo, bar) => {
288 + if (foo) {
289 + useNotAHook(bar)
290 }
291 });
223 - }
224 - `,
225 - `
226 - // This is valid because "use"-prefixed functions called in
227 - // unnamed function arguments are not assumed to be hooks.
228 - React.unknownFunction((foo, bar) => {
229 - if (foo) {
230 - useNotAHook(bar)
231 - }
232 - });
233 - `,
234 - `
235 - // This is valid because "use"-prefixed functions called in
236 - // unnamed function arguments are not assumed to be hooks.
237 - unknownFunction(function(foo, bar) {
238 - if (foo) {
239 - useNotAHook(bar)
240 - }
241 - });
242 - `,
243 - `
244 - // Regression test for incorrectly flagged valid code.
245 - function RegressionTest() {
246 - const foo = cond ? a : b;
247 - useState();
248 - }
249 - `,
250 - `
251 - // Valid because exceptions abort rendering
252 - function RegressionTest() {
253 - if (page == null) {
254 - throw new Error('oh no!');
292 + `,
293 + },
294 + {
295 + code: normalizeIndent`
296 + // This is valid because "use"-prefixed functions called in
297 + // unnamed function arguments are not assumed to be hooks.
298 + unknownFunction(function(foo, bar) {
299 + if (foo) {
300 + useNotAHook(bar)
301 + }
302 + });
303 + `,
304 + },
305 + {
306 + code: normalizeIndent`
307 + // Regression test for incorrectly flagged valid code.
308 + function RegressionTest() {
309 + const foo = cond ? a : b;
310 + useState();
311 }
256 - useState();
257 - }
258 - `,
259 - `
260 - // Valid because the loop doesn't change the order of hooks calls.
261 - function RegressionTest() {
262 - const res = [];
263 - const additionalCond = true;
264 - for (let i = 0; i !== 10 && additionalCond; ++i ) {
265 - res.push(i);
266 - }
267 - React.useLayoutEffect(() => {});
268 - }
269 - `,
270 - `
271 - // Is valid but hard to compute by brute-forcing
272 - function MyComponent() {
273 - // 40 conditions
274 - if (c) {} else {}
275 - if (c) {} else {}
276 - if (c) {} else {}
277 - if (c) {} else {}
278 - if (c) {} else {}
279 - if (c) {} else {}
280 - if (c) {} else {}
281 - if (c) {} else {}
282 - if (c) {} else {}
283 - if (c) {} else {}
284 - if (c) {} else {}
285 - if (c) {} else {}
286 - if (c) {} else {}
287 - if (c) {} else {}
288 - if (c) {} else {}
289 - if (c) {} else {}
290 - if (c) {} else {}
291 - if (c) {} else {}
292 - if (c) {} else {}
293 - if (c) {} else {}
294 - if (c) {} else {}
295 - if (c) {} else {}
296 - if (c) {} else {}
297 - if (c) {} else {}
298 - if (c) {} else {}
299 - if (c) {} else {}
300 - if (c) {} else {}
301 - if (c) {} else {}
302 - if (c) {} else {}
303 - if (c) {} else {}
304 - if (c) {} else {}
305 - if (c) {} else {}
306 - if (c) {} else {}
307 - if (c) {} else {}
308 - if (c) {} else {}
309 - if (c) {} else {}
310 - if (c) {} else {}
311 - if (c) {} else {}
312 - if (c) {} else {}
313 - if (c) {} else {}
312 + `,
313 + },
314 + {
315 + code: normalizeIndent`
316 + // Valid because exceptions abort rendering
317 + function RegressionTest() {
318 + if (page == null) {
319 + throw new Error('oh no!');
320 + }
321 + useState();
322 + }
323 + `,
324 + },
325 + {
326 + code: normalizeIndent`
327 + // Valid because the loop doesn't change the order of hooks calls.
328 + function RegressionTest() {
329 + const res = [];
330 + const additionalCond = true;
331 + for (let i = 0; i !== 10 && additionalCond; ++i ) {
332 + res.push(i);
333 + }
334 + React.useLayoutEffect(() => {});
335 + }
336 + `,
337 + },
338 + {
339 + code: normalizeIndent`
340 + // Is valid but hard to compute by brute-forcing
341 + function MyComponent() {
342 + // 40 conditions
343 + if (c) {} else {}
344 + if (c) {} else {}
345 + if (c) {} else {}
346 + if (c) {} else {}
347 + if (c) {} else {}
348 + if (c) {} else {}
349 + if (c) {} else {}
350 + if (c) {} else {}
351 + if (c) {} else {}
352 + if (c) {} else {}
353 + if (c) {} else {}
354 + if (c) {} else {}
355 + if (c) {} else {}
356 + if (c) {} else {}
357 + if (c) {} else {}
358 + if (c) {} else {}
359 + if (c) {} else {}
360 + if (c) {} else {}
361 + if (c) {} else {}
362 + if (c) {} else {}
363 + if (c) {} else {}
364 + if (c) {} else {}
365 + if (c) {} else {}
366 + if (c) {} else {}
367 + if (c) {} else {}
368 + if (c) {} else {}
369 + if (c) {} else {}
370 + if (c) {} else {}
371 + if (c) {} else {}
372 + if (c) {} else {}
373 + if (c) {} else {}
374 + if (c) {} else {}
375 + if (c) {} else {}
376 + if (c) {} else {}
377 + if (c) {} else {}
378 + if (c) {} else {}
379 + if (c) {} else {}
380 + if (c) {} else {}
381 + if (c) {} else {}
382 + if (c) {} else {}
383
315 - // 10 hooks
316 - useHook();
317 - useHook();
318 - useHook();
319 - useHook();
320 - useHook();
321 - useHook();
322 - useHook();
323 - useHook();
324 - useHook();
325 - useHook();
326 - }
327 - `,
328 - `
329 - // Valid because the neither the conditions before or after the hook affect the hook call
330 - // Failed prior to implementing BigInt because pathsFromStartToEnd and allPathsFromStartToEnd were too big and had rounding errors
331 - const useSomeHook = () => {};
384 + // 10 hooks
385 + useHook();
386 + useHook();
387 + useHook();
388 + useHook();
389 + useHook();
390 + useHook();
391 + useHook();
392 + useHook();
393 + useHook();
394 + useHook();
395 + }
396 + `,
397 + },
398 + {
399 + code: normalizeIndent`
400 + // Valid because the neither the conditions before or after the hook affect the hook call
401 + // Failed prior to implementing BigInt because pathsFromStartToEnd and allPathsFromStartToEnd were too big and had rounding errors
402 + const useSomeHook = () => {};
403
333 - const SomeName = () => {
334 - const filler = FILLER ?? FILLER ?? FILLER;
335 - const filler2 = FILLER ?? FILLER ?? FILLER;
336 - const filler3 = FILLER ?? FILLER ?? FILLER;
337 - const filler4 = FILLER ?? FILLER ?? FILLER;
338 - const filler5 = FILLER ?? FILLER ?? FILLER;
339 - const filler6 = FILLER ?? FILLER ?? FILLER;
340 - const filler7 = FILLER ?? FILLER ?? FILLER;
341 - const filler8 = FILLER ?? FILLER ?? FILLER;
404 + const SomeName = () => {
405 + const filler = FILLER ?? FILLER ?? FILLER;
406 + const filler2 = FILLER ?? FILLER ?? FILLER;
407 + const filler3 = FILLER ?? FILLER ?? FILLER;
408 + const filler4 = FILLER ?? FILLER ?? FILLER;
409 + const filler5 = FILLER ?? FILLER ?? FILLER;
410 + const filler6 = FILLER ?? FILLER ?? FILLER;
411 + const filler7 = FILLER ?? FILLER ?? FILLER;
412 + const filler8 = FILLER ?? FILLER ?? FILLER;
413
343 - useSomeHook();
414 + useSomeHook();
415
345 - if (anyConditionCanEvenBeFalse) {
346 - return null;
347 - }
416 + if (anyConditionCanEvenBeFalse) {
417 + return null;
418 + }
419
349 - return (
350 - <React.Fragment>
351 - {FILLER ? FILLER : FILLER}
352 - {FILLER ? FILLER : FILLER}
353 - {FILLER ? FILLER : FILLER}
354 - {FILLER ? FILLER : FILLER}
355 - {FILLER ? FILLER : FILLER}
356 - {FILLER ? FILLER : FILLER}
357 - {FILLER ? FILLER : FILLER}
358 - {FILLER ? FILLER : FILLER}
359 - {FILLER ? FILLER : FILLER}
360 - {FILLER ? FILLER : FILLER}
361 - {FILLER ? FILLER : FILLER}
362 - {FILLER ? FILLER : FILLER}
363 - {FILLER ? FILLER : FILLER}
364 - {FILLER ? FILLER : FILLER}
365 - {FILLER ? FILLER : FILLER}
366 - {FILLER ? FILLER : FILLER}
367 - {FILLER ? FILLER : FILLER}
368 - {FILLER ? FILLER : FILLER}
369 - {FILLER ? FILLER : FILLER}
370 - {FILLER ? FILLER : FILLER}
371 - {FILLER ? FILLER : FILLER}
372 - {FILLER ? FILLER : FILLER}
373 - {FILLER ? FILLER : FILLER}
374 - {FILLER ? FILLER : FILLER}
375 - {FILLER ? FILLER : FILLER}
376 - {FILLER ? FILLER : FILLER}
377 - {FILLER ? FILLER : FILLER}
378 - {FILLER ? FILLER : FILLER}
379 - {FILLER ? FILLER : FILLER}
380 - {FILLER ? FILLER : FILLER}
381 - {FILLER ? FILLER : FILLER}
382 - {FILLER ? FILLER : FILLER}
383 - {FILLER ? FILLER : FILLER}
384 - {FILLER ? FILLER : FILLER}
385 - {FILLER ? FILLER : FILLER}
386 - {FILLER ? FILLER : FILLER}
387 - {FILLER ? FILLER : FILLER}
388 - {FILLER ? FILLER : FILLER}
389 - {FILLER ? FILLER : FILLER}
390 - {FILLER ? FILLER : FILLER}
391 - {FILLER ? FILLER : FILLER}
392 - {FILLER ? FILLER : FILLER}
393 - </React.Fragment>
394 - );
395 - };
396 - `,
397 - `
398 - // Valid because the neither the condition nor the loop affect the hook call.
399 - function App(props) {
400 - const someObject = {propA: true};
401 - for (const propName in someObject) {
402 - if (propName === true) {
403 - } else {
420 + return (
421 + <React.Fragment>
422 + {FILLER ? FILLER : FILLER}
423 + {FILLER ? FILLER : FILLER}
424 + {FILLER ? FILLER : FILLER}
425 + {FILLER ? FILLER : FILLER}
426 + {FILLER ? FILLER : FILLER}
427 + {FILLER ? FILLER : FILLER}
428 + {FILLER ? FILLER : FILLER}
429 + {FILLER ? FILLER : FILLER}
430 + {FILLER ? FILLER : FILLER}
431 + {FILLER ? FILLER : FILLER}
432 + {FILLER ? FILLER : FILLER}
433 + {FILLER ? FILLER : FILLER}
434 + {FILLER ? FILLER : FILLER}
435 + {FILLER ? FILLER : FILLER}
436 + {FILLER ? FILLER : FILLER}
437 + {FILLER ? FILLER : FILLER}
438 + {FILLER ? FILLER : FILLER}
439 + {FILLER ? FILLER : FILLER}
440 + {FILLER ? FILLER : FILLER}
441 + {FILLER ? FILLER : FILLER}
442 + {FILLER ? FILLER : FILLER}
443 + {FILLER ? FILLER : FILLER}
444 + {FILLER ? FILLER : FILLER}
445 + {FILLER ? FILLER : FILLER}
446 + {FILLER ? FILLER : FILLER}
447 + {FILLER ? FILLER : FILLER}
448 + {FILLER ? FILLER : FILLER}
449 + {FILLER ? FILLER : FILLER}
450 + {FILLER ? FILLER : FILLER}
451 + {FILLER ? FILLER : FILLER}
452 + {FILLER ? FILLER : FILLER}
453 + {FILLER ? FILLER : FILLER}
454 + {FILLER ? FILLER : FILLER}
455 + {FILLER ? FILLER : FILLER}
456 + {FILLER ? FILLER : FILLER}
457 + {FILLER ? FILLER : FILLER}
458 + {FILLER ? FILLER : FILLER}
459 + {FILLER ? FILLER : FILLER}
460 + {FILLER ? FILLER : FILLER}
461 + {FILLER ? FILLER : FILLER}
462 + {FILLER ? FILLER : FILLER}
463 + {FILLER ? FILLER : FILLER}
464 + </React.Fragment>
465 + );
466 + };
467 + `,
468 + },
469 + {
470 + code: normalizeIndent`
471 + // Valid because the neither the condition nor the loop affect the hook call.
472 + function App(props) {
473 + const someObject = {propA: true};
474 + for (const propName in someObject) {
475 + if (propName === true) {
476 + } else {
477 + }
478 }
479 + const [myState, setMyState] = useState(null);
480 }
406 - const [myState, setMyState] = useState(null);
407 - }
408 - `,
481 + `,
482 + },
483 ],
484 invalid: [
485 {
412 - code: `
486 + code: normalizeIndent`
487 // Invalid because it's dangerous and might not warn otherwise.
488 // This *must* be invalid.
489 function ComponentWithConditionalHook() {
@@ -421,7 +495,7 @@ const tests = {
495 errors: [conditionalError('useConditionalHook')],
496 },
497 {
424 - code: `
498 + code: normalizeIndent`
499 Hook.use();
500 Hook._use();
501 Hook.useState();
@@ -437,7 +511,7 @@ const tests = {
511 ],
512 },
513 {
440 - code: `
514 + code: normalizeIndent`
515 class C {
516 m() {
517 This.useHook();
@@ -448,7 +522,7 @@ const tests = {
522 errors: [classError('This.useHook'), classError('Super.useHook')],
523 },
524 {
451 - code: `
525 + code: normalizeIndent`
526 // This is a false positive (it's valid) that unfortunately
527 // we cannot avoid. Prefer to rename it to not start with "use"
528 class Foo extends Component {
@@ -462,7 +536,7 @@ const tests = {
536 errors: [classError('FooStore.useFeatureFlag')],
537 },
538 {
465 - code: `
539 + code: normalizeIndent`
540 // Invalid because it's dangerous and might not warn otherwise.
541 // This *must* be invalid.
542 function ComponentWithConditionalHook() {
@@ -474,7 +548,7 @@ const tests = {
548 errors: [conditionalError('Namespace.useConditionalHook')],
549 },
550 {
477 - code: `
551 + code: normalizeIndent`
552 // Invalid because it's dangerous and might not warn otherwise.
553 // This *must* be invalid.
554 function createComponent() {
@@ -488,7 +562,7 @@ const tests = {
562 errors: [conditionalError('useConditionalHook')],
563 },
564 {
491 - code: `
565 + code: normalizeIndent`
566 // Invalid because it's dangerous and might not warn otherwise.
567 // This *must* be invalid.
568 function useHookWithConditionalHook() {
@@ -500,7 +574,7 @@ const tests = {
574 errors: [conditionalError('useConditionalHook')],
575 },
576 {
503 - code: `
577 + code: normalizeIndent`
578 // Invalid because it's dangerous and might not warn otherwise.
579 // This *must* be invalid.
580 function createHook() {
@@ -514,7 +588,7 @@ const tests = {
588 errors: [conditionalError('useConditionalHook')],
589 },
590 {
517 - code: `
591 + code: normalizeIndent`
592 // Invalid because it's dangerous and might not warn otherwise.
593 // This *must* be invalid.
594 function ComponentWithTernaryHook() {
@@ -524,7 +598,7 @@ const tests = {
598 errors: [conditionalError('useTernaryHook')],
599 },
600 {
527 - code: `
601 + code: normalizeIndent`
602 // Invalid because it's a common misunderstanding.
603 // We *could* make it valid but the runtime error could be confusing.
604 function ComponentWithHookInsideCallback() {
@@ -536,7 +610,7 @@ const tests = {
610 errors: [genericError('useHookInsideCallback')],
611 },
612 {
539 - code: `
613 + code: normalizeIndent`
614 // Invalid because it's a common misunderstanding.
615 // We *could* make it valid but the runtime error could be confusing.
616 function createComponent() {
@@ -550,7 +624,7 @@ const tests = {
624 errors: [genericError('useHookInsideCallback')],
625 },
626 {
553 - code: `
627 + code: normalizeIndent`
628 // Invalid because it's a common misunderstanding.
629 // We *could* make it valid but the runtime error could be confusing.
630 const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
@@ -563,7 +637,7 @@ const tests = {
637 errors: [genericError('useHookInsideCallback')],
638 },
639 {
566 - code: `
640 + code: normalizeIndent`
641 // Invalid because it's a common misunderstanding.
642 // We *could* make it valid but the runtime error could be confusing.
643 const ComponentWithHookInsideCallback = React.memo(props => {
@@ -576,7 +650,7 @@ const tests = {
650 errors: [genericError('useHookInsideCallback')],
651 },
652 {
579 - code: `
653 + code: normalizeIndent`
654 // Invalid because it's a common misunderstanding.
655 // We *could* make it valid but the runtime error could be confusing.
656 function ComponentWithHookInsideCallback() {
@@ -588,7 +662,7 @@ const tests = {
662 errors: [functionError('useState', 'handleClick')],
663 },
664 {
591 - code: `
665 + code: normalizeIndent`
666 // Invalid because it's a common misunderstanding.
667 // We *could* make it valid but the runtime error could be confusing.
668 function createComponent() {
@@ -602,7 +676,7 @@ const tests = {
676 errors: [functionError('useState', 'handleClick')],
677 },
678 {
605 - code: `
679 + code: normalizeIndent`
680 // Invalid because it's dangerous and might not warn otherwise.
681 // This *must* be invalid.
682 function ComponentWithHookInsideLoop() {
@@ -614,7 +688,7 @@ const tests = {
688 errors: [loopError('useHookInsideLoop')],
689 },
690 {
617 - code: `
691 + code: normalizeIndent`
692 // Invalid because it's dangerous and might not warn otherwise.
693 // This *must* be invalid.
694 function renderItem() {
@@ -628,7 +702,7 @@ const tests = {
702 errors: [functionError('useState', 'renderItem')],
703 },
704 {
631 - code: `
705 + code: normalizeIndent`
706 // Currently invalid because it violates the convention and removes the "taint"
707 // from a hook. We *could* make it valid to avoid some false positives but let's
708 // ensure that we don't break the "renderItem" and "normalFunctionWithConditionalHook"
@@ -642,7 +716,7 @@ const tests = {
716 ],
717 },
718 {
645 - code: `
719 + code: normalizeIndent`
720 // These are neither functions nor hooks.
721 function _normalFunctionWithHook() {
722 useHookInsideNormalFunction();
@@ -657,7 +731,7 @@ const tests = {
731 ],
732 },
733 {
660 - code: `
734 + code: normalizeIndent`
735 // Invalid because it's dangerous and might not warn otherwise.
736 // This *must* be invalid.
737 function normalFunctionWithConditionalHook() {
@@ -674,7 +748,7 @@ const tests = {
748 ],
749 },
750 {
677 - code: `
751 + code: normalizeIndent`
752 // Invalid because it's dangerous and might not warn otherwise.
753 // This *must* be invalid.
754 function useHookInLoops() {
@@ -698,7 +772,7 @@ const tests = {
772 ],
773 },
774 {
701 - code: `
775 + code: normalizeIndent`
776 // Invalid because it's dangerous and might not warn otherwise.
777 // This *must* be invalid.
778 function useHookInLoops() {
@@ -712,7 +786,7 @@ const tests = {
786 errors: [loopError('useHook1'), loopError('useHook2', true)],
787 },
788 {
715 - code: `
789 + code: normalizeIndent`
790 // Invalid because it's dangerous and might not warn otherwise.
791 // This *must* be invalid.
792 function useLabeledBlock() {
@@ -725,7 +799,7 @@ const tests = {
799 errors: [conditionalError('useHook')],
800 },
801 {
728 - code: `
802 + code: normalizeIndent`
803 // Currently invalid.
804 // These are variations capturing the current heuristic--
805 // we only allow hooks in PascalCase or useFoo functions.
@@ -754,7 +828,7 @@ const tests = {
828 ],
829 },
830 {
757 - code: `
831 + code: normalizeIndent`
832 // Invalid because it's dangerous and might not warn otherwise.
833 // This *must* be invalid.
834 function useHook() {
@@ -765,7 +839,7 @@ const tests = {
839 errors: [conditionalError('useState', true)],
840 },
841 {
768 - code: `
842 + code: normalizeIndent`
843 // Invalid because it's dangerous and might not warn otherwise.
844 // This *must* be invalid.
845 function useHook() {
@@ -781,7 +855,7 @@ const tests = {
855 errors: [conditionalError('useState', true)],
856 },
857 {
784 - code: `
858 + code: normalizeIndent`
859 // Invalid because it's dangerous and might not warn otherwise.
860 // This *must* be invalid.
861 function useHook() {
@@ -797,7 +871,7 @@ const tests = {
871 errors: [conditionalError('useState', true)],
872 },
873 {
800 - code: `
874 + code: normalizeIndent`
875 // Invalid because it's dangerous and might not warn otherwise.
876 // This *must* be invalid.
877 function useHook() {
@@ -808,7 +882,7 @@ const tests = {
882 errors: [conditionalError('useHook1'), conditionalError('useHook2')],
883 },
884 {
811 - code: `
885 + code: normalizeIndent`
886 // Invalid because it's dangerous and might not warn otherwise.
887 // This *must* be invalid.
888 function useHook() {
@@ -824,7 +898,7 @@ const tests = {
898 ],
899 },
900 {
827 - code: `
901 + code: normalizeIndent`
902 // Invalid because it's dangerous and might not warn otherwise.
903 // This *must* be invalid.
904 function useHook({ bar }) {
@@ -840,7 +914,7 @@ const tests = {
914 ],
915 },
916 {
843 - code: `
917 + code: normalizeIndent`
918 // Invalid because it's dangerous and might not warn otherwise.
919 // This *must* be invalid.
920 const FancyButton = React.forwardRef((props, ref) => {
@@ -853,7 +927,7 @@ const tests = {
927 errors: [conditionalError('useCustomHook')],
928 },
929 {
856 - code: `
930 + code: normalizeIndent`
931 // Invalid because it's dangerous and might not warn otherwise.
932 // This *must* be invalid.
933 const FancyButton = forwardRef(function(props, ref) {
@@ -866,7 +940,7 @@ const tests = {
940 errors: [conditionalError('useCustomHook')],
941 },
942 {
869 - code: `
943 + code: normalizeIndent`
944 // Invalid because it's dangerous and might not warn otherwise.
945 // This *must* be invalid.
946 const MemoizedButton = memo(function(props) {
@@ -879,7 +953,7 @@ const tests = {
953 errors: [conditionalError('useCustomHook')],
954 },
955 {
882 - code: `
956 + code: normalizeIndent`
957 // This is invalid because "use"-prefixed functions used in named
958 // functions are assumed to be hooks.
959 React.unknownFunction(function notAComponent(foo, bar) {
@@ -889,7 +963,7 @@ const tests = {
963 errors: [functionError('useProbablyAHook', 'notAComponent')],
964 },
965 {
892 - code: `
966 + code: normalizeIndent`
967 // Invalid because it's dangerous.
968 // Normally, this would crash, but not if you use inline requires.
969 // This *must* be invalid.
@@ -909,7 +983,7 @@ const tests = {
983 ],
984 },
985 {
912 - code: `
986 + code: normalizeIndent`
987 // Technically this is a false positive.
988 // We *could* make it valid (and it used to be).
989 //
@@ -926,7 +1000,7 @@ const tests = {
1000 errors: [topLevelError('useBasename')],
1001 },
1002 {
929 - code: `
1003 + code: normalizeIndent`
1004 class ClassComponentWithFeatureFlag extends React.Component {
1005 render() {
1006 if (foo) {
@@ -938,7 +1012,7 @@ const tests = {
1012 errors: [classError('useFeatureFlag')],
1013 },
1014 {
941 - code: `
1015 + code: normalizeIndent`
1016 class ClassComponentWithHook extends React.Component {
1017 render() {
1018 React.useState();
@@ -948,25 +1022,25 @@ const tests = {
1022 errors: [classError('React.useState')],
1023 },
1024 {
951 - code: `
1025 + code: normalizeIndent`
1026 (class {useHook = () => { useState(); }});
1027 `,
1028 errors: [classError('useState')],
1029 },
1030 {
957 - code: `
1031 + code: normalizeIndent`
1032 (class {useHook() { useState(); }});
1033 `,
1034 errors: [classError('useState')],
1035 },
1036 {
963 - code: `
1037 + code: normalizeIndent`
1038 (class {h = () => { useState(); }});
1039 `,
1040 errors: [classError('useState')],
1041 },
1042 {
969 - code: `
1043 + code: normalizeIndent`
1044 (class {i() { useState(); }});
1045 `,
1046 errors: [classError('useState')],
@@ -977,92 +1051,106 @@ const tests = {
1051 if (__EXPERIMENTAL__) {
1052 tests.valid = [
1053 ...tests.valid,
980 - `
981 - // Valid because functions created with useEvent can be called in a useEffect.
982 - function MyComponent({ theme }) {
983 - const onClick = useEvent(() => {
984 - showNotification(theme);
985 - });
986 - useEffect(() => {
987 - onClick();
988 - });
989 - }
990 - `,
991 - `
992 - // Valid because functions created with useEvent can be called in closures.
993 - function MyComponent({ theme }) {
994 - const onClick = useEvent(() => {
995 - showNotification(theme);
996 - });
997 - return <Child onClick={() => onClick()}></Child>;
998 - }
999 - `,
1000 - `
1001 - // Valid because functions created with useEvent can be called in closures.
1002 - function MyComponent({ theme }) {
1003 - const onClick = useEvent(() => {
1004 - showNotification(theme);
1005 - });
1006 - const onClick2 = () => { onClick() };
1007 - const onClick3 = useCallback(() => onClick(), []);
1008 - return <>
1009 - <Child onClick={onClick2}></Child>
1010 - <Child onClick={onClick3}></Child>
1011 - </>;
1012 - }
1013 - `,
1014 - `
1015 - // Valid because functions created with useEvent can be passed by reference in useEffect
1016 - // and useEvent.
1017 - function MyComponent({ theme }) {
1018 - const onClick = useEvent(() => {
1019 - showNotification(theme);
1020 - });
1021 - const onClick2 = useEvent(() => {
1022 - debounce(onClick);
1023 - });
1024 - useEffect(() => {
1025 - let id = setInterval(onClick, 100);
1026 - return () => clearInterval(onClick);
1027 - }, []);
1028 - return <Child onClick={() => onClick2()} />
1029 - }
1030 - `,
1031 - `
1032 - const MyComponent = ({theme}) => {
1033 - const onClick = useEvent(() => {
1034 - showNotification(theme);
1035 - });
1036 - return <Child onClick={() => onClick()}></Child>;
1037 - };
1038 - `,
1039 - `
1040 - function MyComponent({ theme }) {
1041 - const notificationService = useNotifications();
1042 - const showNotification = useEvent((text) => {
1043 - notificationService.notify(theme, text);
1044 - });
1045 - const onClick = useEvent((text) => {
1046 - showNotification(text);
1047 - });
1048 - return <Child onClick={(text) => onClick(text)} />
1049 - }
1050 - `,
1051 - `
1052 - function MyComponent({ theme }) {
1053 - useEffect(() => {
1054 - onClick();
1055 - });
1056 - const onClick = useEvent(() => {
1057 - showNotification(theme);
1058 - });
1059 - }
1060 - `,
1054 + {
1055 + code: normalizeIndent`
1056 + // Valid because functions created with useEvent can be called in a useEffect.
1057 + function MyComponent({ theme }) {
1058 + const onClick = useEvent(() => {
1059 + showNotification(theme);
1060 + });
1061 + useEffect(() => {
1062 + onClick();
1063 + });
1064 + }
1065 + `,
1066 + },
1067 + {
1068 + code: normalizeIndent`
1069 + // Valid because functions created with useEvent can be called in closures.
1070 + function MyComponent({ theme }) {
1071 + const onClick = useEvent(() => {
1072 + showNotification(theme);
1073 + });
1074 + return <Child onClick={() => onClick()}></Child>;
1075 + }
1076 + `,
1077 + },
1078 + {
1079 + code: normalizeIndent`
1080 + // Valid because functions created with useEvent can be called in closures.
1081 + function MyComponent({ theme }) {
1082 + const onClick = useEvent(() => {
1083 + showNotification(theme);
1084 + });
1085 + const onClick2 = () => { onClick() };
1086 + const onClick3 = useCallback(() => onClick(), []);
1087 + return <>
1088 + <Child onClick={onClick2}></Child>
1089 + <Child onClick={onClick3}></Child>
1090 + </>;
1091 + }
1092 + `,
1093 + },
1094 + {
1095 + code: normalizeIndent`
1096 + // Valid because functions created with useEvent can be passed by reference in useEffect
1097 + // and useEvent.
1098 + function MyComponent({ theme }) {
1099 + const onClick = useEvent(() => {
1100 + showNotification(theme);
1101 + });
1102 + const onClick2 = useEvent(() => {
1103 + debounce(onClick);
1104 + });
1105 + useEffect(() => {
1106 + let id = setInterval(onClick, 100);
1107 + return () => clearInterval(onClick);
1108 + }, []);
1109 + return <Child onClick={() => onClick2()} />
1110 + }
1111 + `,
1112 + },
1113 + {
1114 + code: normalizeIndent`
1115 + const MyComponent = ({theme}) => {
1116 + const onClick = useEvent(() => {
1117 + showNotification(theme);
1118 + });
1119 + return <Child onClick={() => onClick()}></Child>;
1120 + };
1121 + `,
1122 + },
1123 + {
1124 + code: normalizeIndent`
1125 + function MyComponent({ theme }) {
1126 + const notificationService = useNotifications();
1127 + const showNotification = useEvent((text) => {
1128 + notificationService.notify(theme, text);
1129 + });
1130 + const onClick = useEvent((text) => {
1131 + showNotification(text);
1132 + });
1133 + return <Child onClick={(text) => onClick(text)} />
1134 + }
1135 + `,
1136 + },
1137 + {
1138 + code: normalizeIndent`
1139 + function MyComponent({ theme }) {
1140 + useEffect(() => {
1141 + onClick();
1142 + });
1143 + const onClick = useEvent(() => {
1144 + showNotification(theme);
1145 + });
1146 + }
1147 + `,
1148 + },
1149 ];
1150 tests.invalid = [
1151 ...tests.invalid,
1152 {
1065 - code: `
1153 + code: normalizeIndent`
1154 function MyComponent({ theme }) {
1155 const onClick = useEvent(() => {
1156 showNotification(theme);
@@ -1073,7 +1161,7 @@ if (__EXPERIMENTAL__) {
1161 errors: [useEventError('onClick')],
1162 },
1163 {
1076 - code: `
1164 + code: normalizeIndent`
1165 // This should error even though it shares an identifier name with the below
1166 function MyComponent({theme}) {
1167 const onClick = useEvent(() => {
@@ -1093,7 +1181,7 @@ if (__EXPERIMENTAL__) {
1181 errors: [{...useEventError('onClick'), line: 7}],
1182 },
1183 {
1096 - code: `
1184 + code: normalizeIndent`
1185 const MyComponent = ({ theme }) => {
1186 const onClick = useEvent(() => {
1187 showNotification(theme);
@@ -1104,7 +1192,7 @@ if (__EXPERIMENTAL__) {
1192 errors: [useEventError('onClick')],
1193 },
1194 {
1107 - code: `
1195 + code: normalizeIndent`
1196 // Invalid because onClick is being aliased to foo but not invoked
1197 function MyComponent({ theme }) {
1198 const onClick = useEvent(() => {
@@ -1117,7 +1205,7 @@ if (__EXPERIMENTAL__) {
1205 errors: [{...useEventError('onClick'), line: 7}],
1206 },
1207 {
1120 - code: `
1208 + code: normalizeIndent`
1209 // Should error because it's being passed down to JSX, although it's been referenced once
1210 // in an effect
1211 function MyComponent({ theme }) {