@codelaw/rules-react 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/NOTICE +10 -0
- package/README.md +46 -2
- package/codelaw-pack.toml +6 -0
- package/data/react/dom-property-aliases.json +131 -0
- package/package.json +14 -4
- package/presets/react/conventions.toml +5 -0
- package/presets/react/recommended.toml +22 -0
- package/rules/react/display-name.md +91 -0
- package/rules/react/exhaustive-deps.md +116 -0
- package/rules/react/hook-prefix.md +108 -0
- package/rules/react/jsx-key.md +99 -0
- package/rules/react/jsx-no-comment-textnodes.md +87 -0
- package/rules/react/jsx-no-duplicate-props.md +66 -0
- package/rules/react/jsx-no-target-blank.md +78 -0
- package/rules/react/jsx-no-undef.md +78 -0
- package/rules/react/no-children-prop.md +80 -0
- package/rules/react/no-danger-with-children.md +83 -0
- package/rules/react/no-deprecated.md +84 -0
- package/rules/react/no-direct-mutation-state.md +102 -0
- package/rules/react/no-find-dom-node.md +72 -0
- package/rules/react/no-is-mounted.md +109 -0
- package/rules/react/no-render-return-value.md +75 -0
- package/rules/react/no-string-refs.md +72 -0
- package/rules/react/no-unescaped-entities.md +69 -0
- package/rules/react/no-unknown-property.md +69 -0
- package/rules/react/props-type-name.md +43 -0
- package/rules/react/require-render-return.md +91 -0
- package/rules/react/rules-of-hooks.md +128 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-find-dom-node
|
|
3
|
+
title: "No findDOMNode"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "a ref has to be attached to the right element by hand"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-find-dom-node]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Code doesn't call `findDOMNode`.
|
|
13
|
+
|
|
14
|
+
**Why:** `findDOMNode` breaks component abstraction, doesn't work with function components, and is
|
|
15
|
+
removed in React 19.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
match:
|
|
19
|
+
any:
|
|
20
|
+
- pattern: findDOMNode($$$A)
|
|
21
|
+
- pattern: $R.findDOMNode($$$A)
|
|
22
|
+
message: "`findDOMNode` is deprecated; use a ref"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## How to fix
|
|
26
|
+
|
|
27
|
+
1. Attach a ref to the element you need (`ref={this.ref}`).
|
|
28
|
+
2. Read `ref.current` instead of calling `findDOMNode`.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
|
|
32
|
+
### JavaScript
|
|
33
|
+
|
|
34
|
+
#### Don't
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
const el = ReactDOM.findDOMNode(this);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Do
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
const el = this.ref.current;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### JSX
|
|
47
|
+
|
|
48
|
+
#### Don't
|
|
49
|
+
|
|
50
|
+
```jsx
|
|
51
|
+
const el = ReactDOM.findDOMNode(this);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Do
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
const el = this.ref.current;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### TSX
|
|
61
|
+
|
|
62
|
+
#### Don't
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
const el = ReactDOM.findDOMNode(this);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### Do
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
const el = this.ref.current;
|
|
72
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-is-mounted
|
|
3
|
+
title: "No this.isMounted()"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the callback has to be cancelled where it starts"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-is-mounted]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Code doesn't call `this.isMounted()`.
|
|
13
|
+
|
|
14
|
+
**Why:** `isMounted` hides leaks: a callback that outlives the component should be cancelled
|
|
15
|
+
instead.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
match:
|
|
19
|
+
pattern: this.isMounted()
|
|
20
|
+
message: "`isMounted` is an anti-pattern and gone from ES6 classes"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## How to fix
|
|
24
|
+
|
|
25
|
+
1. Cancel pending work in `componentWillUnmount`.
|
|
26
|
+
2. Remove the `isMounted` check.
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
### JavaScript
|
|
31
|
+
|
|
32
|
+
#### Don't
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
class A extends React.Component {
|
|
36
|
+
done() {
|
|
37
|
+
if (this.isMounted()) {
|
|
38
|
+
this.setState({ ok: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### Do
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
class A extends React.Component {
|
|
48
|
+
componentWillUnmount() {
|
|
49
|
+
this.request.cancel();
|
|
50
|
+
}
|
|
51
|
+
done() {
|
|
52
|
+
this.setState({ ok: true });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### JSX
|
|
58
|
+
|
|
59
|
+
#### Don't
|
|
60
|
+
|
|
61
|
+
```jsx
|
|
62
|
+
class A extends React.Component {
|
|
63
|
+
done() {
|
|
64
|
+
if (this.isMounted()) {
|
|
65
|
+
this.setState({ ok: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Do
|
|
72
|
+
|
|
73
|
+
```jsx
|
|
74
|
+
class A extends React.Component {
|
|
75
|
+
componentWillUnmount() {
|
|
76
|
+
this.request.cancel();
|
|
77
|
+
}
|
|
78
|
+
done() {
|
|
79
|
+
this.setState({ ok: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### TSX
|
|
85
|
+
|
|
86
|
+
#### Don't
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
class A extends React.Component {
|
|
90
|
+
done() {
|
|
91
|
+
if (this.isMounted()) {
|
|
92
|
+
this.setState({ ok: true });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Do
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
class A extends React.Component {
|
|
102
|
+
componentWillUnmount() {
|
|
103
|
+
this.request.cancel();
|
|
104
|
+
}
|
|
105
|
+
done() {
|
|
106
|
+
this.setState({ ok: true });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-render-return-value
|
|
3
|
+
title: "The return value of ReactDOM.render is not used"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "code that used the instance needs a ref instead"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-render-return-value]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
The value `ReactDOM.render` returns is not stored, returned or passed on.
|
|
13
|
+
|
|
14
|
+
**Why:** It's a legacy handle that is `null` in some cases and gone in newer APIs.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
match:
|
|
18
|
+
pattern: ReactDOM.render($$$A)
|
|
19
|
+
inside:
|
|
20
|
+
stopBy: neighbor
|
|
21
|
+
any:
|
|
22
|
+
- kind: variable_declarator
|
|
23
|
+
- kind: return_statement
|
|
24
|
+
- kind: assignment_expression
|
|
25
|
+
- kind: arguments
|
|
26
|
+
message: "Don't use what `ReactDOM.render` returns; use a ref"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## How to fix
|
|
30
|
+
|
|
31
|
+
1. Pass a ref to the root element and use it instead of the return value.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
### JavaScript
|
|
36
|
+
|
|
37
|
+
#### Don't
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const app = ReactDOM.render(<App />, root);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### Do
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
ReactDOM.render(<App ref={appRef} />, root);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### JSX
|
|
50
|
+
|
|
51
|
+
#### Don't
|
|
52
|
+
|
|
53
|
+
```jsx
|
|
54
|
+
const app = ReactDOM.render(<App />, root);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Do
|
|
58
|
+
|
|
59
|
+
```jsx
|
|
60
|
+
ReactDOM.render(<App ref={appRef} />, root);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### TSX
|
|
64
|
+
|
|
65
|
+
#### Don't
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
const app = ReactDOM.render(<App />, root);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Do
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
ReactDOM.render(<App ref={appRef} />, root);
|
|
75
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-string-refs
|
|
3
|
+
title: "No string refs"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the ref object has to be created where the component can reach it"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-string-refs]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Refs are objects or callbacks, never strings (`ref="input"`, `this.refs.input`).
|
|
13
|
+
|
|
14
|
+
**Why:** String refs are legacy, slow, and removed in React 19.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
match:
|
|
18
|
+
any:
|
|
19
|
+
- kind: jsx_attribute
|
|
20
|
+
regex: ^ref=["']
|
|
21
|
+
- pattern: this.refs.$X
|
|
22
|
+
message: "String refs are legacy; use `useRef`, `createRef` or a callback ref"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## How to fix
|
|
26
|
+
|
|
27
|
+
1. Create a ref with `useRef` or `createRef`.
|
|
28
|
+
2. Pass it as `ref={…}` and read `.current`.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
|
|
32
|
+
### JavaScript
|
|
33
|
+
|
|
34
|
+
#### Don't
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
const a = <input ref="name" />;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Do
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
const a = <input ref={nameRef} />;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### JSX
|
|
47
|
+
|
|
48
|
+
#### Don't
|
|
49
|
+
|
|
50
|
+
```jsx
|
|
51
|
+
const a = <input ref="name" />;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Do
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
const a = <input ref={nameRef} />;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### TSX
|
|
61
|
+
|
|
62
|
+
#### Don't
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
const a = <input ref="name" />;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### Do
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
const a = <input ref={nameRef} />;
|
|
72
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-unescaped-entities
|
|
3
|
+
title: "No unescaped >, \", ' or } in JSX text"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the right escape depends on the character and the author's style"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-unescaped-entities]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
JSX text doesn't contain `>`, `"`, `'` or `}` unescaped.
|
|
13
|
+
|
|
14
|
+
**Why:** These characters are easy to misread as markup or code, and a stray `}` is usually a typo.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
match:
|
|
18
|
+
kind: jsx_text
|
|
19
|
+
regex: '[>"''}]'
|
|
20
|
+
message: "Escape this character in JSX text, e.g. `'` or `{\"'\"}`"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## How to fix
|
|
24
|
+
|
|
25
|
+
1. Use an HTML entity (`'`, `"`, `>`) or a string expression (`{"'"}`).
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### JavaScript
|
|
30
|
+
|
|
31
|
+
#### Don't
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const a = <p>Don't stop</p>;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Do
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const a = <p>Don't stop</p>;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### JSX
|
|
44
|
+
|
|
45
|
+
#### Don't
|
|
46
|
+
|
|
47
|
+
```jsx
|
|
48
|
+
const a = <p>Don't stop</p>;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Do
|
|
52
|
+
|
|
53
|
+
```jsx
|
|
54
|
+
const a = <p>Don't stop</p>;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### TSX
|
|
58
|
+
|
|
59
|
+
#### Don't
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const a = <p>Don't stop</p>;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Do
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
const a = <p>Don't stop</p>;
|
|
69
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-unknown-property
|
|
3
|
+
title: "DOM elements use React's prop names, not HTML attribute names"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: auto
|
|
6
|
+
stability: preview
|
|
7
|
+
ported_from: [eslint-plugin-react/no-unknown-property]
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
A DOM element in JSX uses React's names for attributes: `className` not `class`, `htmlFor` not
|
|
12
|
+
`for`, `onClick` not `onclick`, `tabIndex` not `tabindex`.
|
|
13
|
+
|
|
14
|
+
**Why:** React ignores or warns about the HTML spellings, so the attribute silently does nothing.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: react/no-unknown-property
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### JavaScript
|
|
23
|
+
|
|
24
|
+
#### Don't
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const a = <input tabindex="1" onclick={go} />;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Do
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const a = <input tabIndex="1" onClick={go} />;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### JSX
|
|
37
|
+
|
|
38
|
+
#### Don't
|
|
39
|
+
|
|
40
|
+
```jsx
|
|
41
|
+
const a = <input tabindex="1" onclick={go} />;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### Do
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
const a = <input tabIndex="1" onClick={go} />;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### TSX
|
|
51
|
+
|
|
52
|
+
#### Don't
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
const a = <label class="x" for="name" onclick={go}>Name</label>;
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Do
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
const a = <label className="x" htmlFor="name" onClick={go}>Name</label>;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Differences from the source
|
|
65
|
+
|
|
66
|
+
Only HTML spellings React renames are checked (the pack's `react/dom-property-aliases` data table);
|
|
67
|
+
unknown attributes in general are not. In `.js` and `.jsx` files the JavaScript grammar can't parse
|
|
68
|
+
`class=` and `for=` as JSX attributes (they are reserved words), so those two are found only in
|
|
69
|
+
`.tsx`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/props-type-name
|
|
3
|
+
title: "A component's props type is named <Component>Props"
|
|
4
|
+
languages: [tsx]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "renaming a type other files import would break them, so it is offered, not applied"
|
|
7
|
+
stability: preview
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
The type of a component's props is named after the component: `CardProps` for `Card`.
|
|
12
|
+
|
|
13
|
+
**Why:** One naming pattern makes props types easy to find and import.
|
|
14
|
+
|
|
15
|
+
```codelaw
|
|
16
|
+
native: react/props-type-name
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### TSX
|
|
22
|
+
|
|
23
|
+
#### Don't
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
type CardOptions = { title: string };
|
|
27
|
+
export function Card(props: CardOptions) {
|
|
28
|
+
return <h2>{props.title}</h2>;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
#### Do
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
type CardProps = { title: string };
|
|
36
|
+
export function Card(props: CardProps) {
|
|
37
|
+
return <h2>{props.title}</h2>;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Differences from the source
|
|
42
|
+
|
|
43
|
+
The type is renamed with its uses in the file; an exported type is reported without a fix.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/require-render-return
|
|
3
|
+
title: "A class component's render() returns"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "what to render needs the author"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/require-render-return]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
The `render()` method of a class component returns a value on every path.
|
|
13
|
+
|
|
14
|
+
**Why:** A `render()` that falls off its end returns `undefined`, which is almost always a forgotten
|
|
15
|
+
`return`.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
native: react/require-render-return
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## How to fix
|
|
22
|
+
|
|
23
|
+
1. Return the element, or `null` to render nothing, on every path.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### JavaScript
|
|
28
|
+
|
|
29
|
+
#### Don't
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
class Title extends React.Component {
|
|
33
|
+
render() {
|
|
34
|
+
<h1>{this.props.text}</h1>;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Do
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
class Title extends React.Component {
|
|
43
|
+
render() {
|
|
44
|
+
return <h1>{this.props.text}</h1>;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### JSX
|
|
50
|
+
|
|
51
|
+
#### Don't
|
|
52
|
+
|
|
53
|
+
```jsx
|
|
54
|
+
class Title extends React.Component {
|
|
55
|
+
render() {
|
|
56
|
+
<h1>{this.props.text}</h1>;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### Do
|
|
62
|
+
|
|
63
|
+
```jsx
|
|
64
|
+
class Title extends React.Component {
|
|
65
|
+
render() {
|
|
66
|
+
return <h1>{this.props.text}</h1>;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### TSX
|
|
72
|
+
|
|
73
|
+
#### Don't
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
class Title extends React.Component {
|
|
77
|
+
render() {
|
|
78
|
+
<h1>{this.props.text}</h1>;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Do
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
class Title extends React.Component {
|
|
87
|
+
render() {
|
|
88
|
+
return <h1>{this.props.text}</h1>;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/rules-of-hooks
|
|
3
|
+
title: "Hooks are called at the top level of components and hooks"
|
|
4
|
+
languages: [javascript, jsx, typescript, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "moving a hook can change what the code does on the renders that skipped it"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react-hooks/rules-of-hooks]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Hooks (`useState`, `useEffect`, any `use…`) are called only at the top level of a function component
|
|
13
|
+
or a custom hook: not in conditions, loops, nested functions, after an early return, in classes, or
|
|
14
|
+
in plain functions.
|
|
15
|
+
|
|
16
|
+
**Why:** React matches hook calls to state by their order, so a hook that runs on some renders and
|
|
17
|
+
not others mixes up every hook after it.
|
|
18
|
+
|
|
19
|
+
```codelaw
|
|
20
|
+
native: react/rules-of-hooks
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## How to fix
|
|
24
|
+
|
|
25
|
+
1. Move the hook call to the top level of the component or hook, before any early return.
|
|
26
|
+
2. Put the condition inside the hook (e.g. inside the effect) instead of around it.
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
### JavaScript
|
|
31
|
+
|
|
32
|
+
#### Don't
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
function Profile({ id }) {
|
|
36
|
+
if (!id) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const [user] = useState(null);
|
|
40
|
+
return user;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### Do
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
function Profile({ id }) {
|
|
48
|
+
const [user] = useState(null);
|
|
49
|
+
if (!id) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return user;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### JSX
|
|
57
|
+
|
|
58
|
+
#### Don't
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
function Profile({ id }) {
|
|
62
|
+
if (!id) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const [user] = useState(null);
|
|
66
|
+
return user;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Do
|
|
71
|
+
|
|
72
|
+
```jsx
|
|
73
|
+
function Profile({ id }) {
|
|
74
|
+
const [user] = useState(null);
|
|
75
|
+
if (!id) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return user;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### TypeScript
|
|
83
|
+
|
|
84
|
+
#### Don't
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
function useProfile(id: string) {
|
|
88
|
+
if (!id) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return useState(id);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Do
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
function useProfile(id: string) {
|
|
99
|
+
const state = useState(id);
|
|
100
|
+
return id ? state : null;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### TSX
|
|
105
|
+
|
|
106
|
+
#### Don't
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
function Profile({ id }) {
|
|
110
|
+
if (!id) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const [user] = useState(null);
|
|
114
|
+
return user;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Do
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
function Profile({ id }) {
|
|
122
|
+
const [user] = useState(null);
|
|
123
|
+
if (!id) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return user;
|
|
127
|
+
}
|
|
128
|
+
```
|