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 }) {