@codelaw/rules-react 0.0.0-stage → 0.2.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 +50 -2
- package/codelaw-pack.toml +6 -0
- package/corpus.toml +25 -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,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/jsx-key
|
|
3
|
+
title: "Elements in an array or a map() callback have a key"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the key must be a stable identity of the item, which only the author knows"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/jsx-key]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
An element written in an array literal, or returned by a `.map()` callback, has a `key` prop.
|
|
13
|
+
|
|
14
|
+
**Why:** Without keys, React can't match list items between renders, which loses state and re-
|
|
15
|
+
renders more than it must.
|
|
16
|
+
|
|
17
|
+
```codelaw
|
|
18
|
+
match:
|
|
19
|
+
any:
|
|
20
|
+
- kind: jsx_element
|
|
21
|
+
- kind: jsx_self_closing_element
|
|
22
|
+
not:
|
|
23
|
+
any:
|
|
24
|
+
- has: { stopBy: neighbor, kind: jsx_attribute, regex: '^key\b' }
|
|
25
|
+
- has: { field: open_tag, has: { stopBy: neighbor, kind: jsx_attribute, regex: '^key\b' } }
|
|
26
|
+
- has: { stopBy: neighbor, kind: jsx_expression, regex: '^\{\s*\.\.\.' }
|
|
27
|
+
- has: { field: open_tag, has: { stopBy: neighbor, kind: jsx_expression, regex: '^\{\s*\.\.\.' } }
|
|
28
|
+
inside:
|
|
29
|
+
stopBy: neighbor
|
|
30
|
+
any:
|
|
31
|
+
- kind: array
|
|
32
|
+
- kind: arrow_function
|
|
33
|
+
inside:
|
|
34
|
+
stopBy: neighbor
|
|
35
|
+
kind: arguments
|
|
36
|
+
inside: { stopBy: neighbor, kind: call_expression, has: { field: function, kind: member_expression, regex: '\.map$' } }
|
|
37
|
+
- kind: parenthesized_expression
|
|
38
|
+
inside:
|
|
39
|
+
stopBy: neighbor
|
|
40
|
+
kind: arrow_function
|
|
41
|
+
inside:
|
|
42
|
+
stopBy: neighbor
|
|
43
|
+
kind: arguments
|
|
44
|
+
inside: { stopBy: neighbor, kind: call_expression, has: { field: function, kind: member_expression, regex: '\.map$' } }
|
|
45
|
+
message: "An element in a list needs a `key` so React can tell its items apart"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## How to fix
|
|
49
|
+
|
|
50
|
+
1. Give the element a `key` that identifies the item, such as its id.
|
|
51
|
+
2. Avoid the array index if items can be reordered or removed.
|
|
52
|
+
|
|
53
|
+
## Examples
|
|
54
|
+
|
|
55
|
+
### JavaScript
|
|
56
|
+
|
|
57
|
+
#### Don't
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
const items = todos.map((todo) => <li>{todo.text}</li>);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### Do
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
const items = todos.map((todo) => <li key={todo.id}>{todo.text}</li>);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### JSX
|
|
70
|
+
|
|
71
|
+
#### Don't
|
|
72
|
+
|
|
73
|
+
```jsx
|
|
74
|
+
const items = todos.map((todo) => <li>{todo.text}</li>);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Do
|
|
78
|
+
|
|
79
|
+
```jsx
|
|
80
|
+
const items = todos.map((todo) => <li key={todo.id}>{todo.text}</li>);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### TSX
|
|
84
|
+
|
|
85
|
+
#### Don't
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
const items = todos.map((todo) => <li>{todo.text}</li>);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### Do
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
const items = todos.map((todo) => <li key={todo.id}>{todo.text}</li>);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Differences from the source
|
|
98
|
+
|
|
99
|
+
Elements returned from a `.map()` callback's block body (`return <li/>`) are not checked yet.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/jsx-no-comment-textnodes
|
|
3
|
+
title: "No comment syntax as text inside JSX"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "whether the text is a comment or content needs the author"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/jsx-no-comment-textnodes]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Comments inside JSX are written as `{/* … */}`; `// …` and `/* … */` in JSX text are not.
|
|
13
|
+
|
|
14
|
+
**Why:** In JSX, `// note` is text that ends up on the page.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
match:
|
|
18
|
+
kind: jsx_text
|
|
19
|
+
regex: ^\s*(//|/\*)
|
|
20
|
+
message: "This is rendered as text, not a comment; write `{/* … */}`"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## How to fix
|
|
24
|
+
|
|
25
|
+
1. Wrap the comment in braces: `{/* … */}`.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### JavaScript
|
|
30
|
+
|
|
31
|
+
#### Don't
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const a = <div>
|
|
35
|
+
// the title
|
|
36
|
+
<h1>Hi</h1>
|
|
37
|
+
</div>;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Do
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
const a = <div>
|
|
44
|
+
{/* the title */}
|
|
45
|
+
<h1>Hi</h1>
|
|
46
|
+
</div>;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### JSX
|
|
50
|
+
|
|
51
|
+
#### Don't
|
|
52
|
+
|
|
53
|
+
```jsx
|
|
54
|
+
const a = <div>
|
|
55
|
+
// the title
|
|
56
|
+
<h1>Hi</h1>
|
|
57
|
+
</div>;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Do
|
|
61
|
+
|
|
62
|
+
```jsx
|
|
63
|
+
const a = <div>
|
|
64
|
+
{/* the title */}
|
|
65
|
+
<h1>Hi</h1>
|
|
66
|
+
</div>;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### TSX
|
|
70
|
+
|
|
71
|
+
#### Don't
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
const a = <div>
|
|
75
|
+
// the title
|
|
76
|
+
<h1>Hi</h1>
|
|
77
|
+
</div>;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Do
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
const a = <div>
|
|
84
|
+
{/* the title */}
|
|
85
|
+
<h1>Hi</h1>
|
|
86
|
+
</div>;
|
|
87
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/jsx-no-duplicate-props
|
|
3
|
+
title: "No prop given twice on one element"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which value should win needs the author"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/jsx-no-duplicate-props]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A JSX element gives each prop once.
|
|
13
|
+
|
|
14
|
+
**Why:** The last value silently wins, so the first is dead code or a mistake.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: react/jsx-no-duplicate-props
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Keep the value that is right and remove the other.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### JavaScript
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
const a = <input type="text" type="email" />;
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
const a = <input type="email" />;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### JSX
|
|
41
|
+
|
|
42
|
+
#### Don't
|
|
43
|
+
|
|
44
|
+
```jsx
|
|
45
|
+
const a = <input type="text" type="email" />;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
#### Do
|
|
49
|
+
|
|
50
|
+
```jsx
|
|
51
|
+
const a = <input type="email" />;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### TSX
|
|
55
|
+
|
|
56
|
+
#### Don't
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
const a = <input type="text" type="email" />;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### Do
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
const a = <input type="email" />;
|
|
66
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/jsx-no-target-blank
|
|
3
|
+
title: "Links that open a new tab set rel=\"noreferrer\""
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: auto
|
|
6
|
+
stability: preview
|
|
7
|
+
ported_from: [eslint-plugin-react/jsx-no-target-blank]
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
An `<a target="_blank">` with an `href` also has `rel="noreferrer"`.
|
|
12
|
+
|
|
13
|
+
**Why:** Without it, older browsers give the opened page a handle on this one (`window.opener`), and
|
|
14
|
+
the referrer leaks.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
match:
|
|
18
|
+
kind: jsx_attribute
|
|
19
|
+
regex: ^target=["']_blank["']$
|
|
20
|
+
inside:
|
|
21
|
+
stopBy: neighbor
|
|
22
|
+
any: [{ kind: jsx_opening_element }, { kind: jsx_self_closing_element }]
|
|
23
|
+
all:
|
|
24
|
+
- has: { field: name, regex: ^a$ }
|
|
25
|
+
- has: { stopBy: neighbor, kind: jsx_attribute, regex: ^href= }
|
|
26
|
+
- not: { has: { stopBy: neighbor, kind: jsx_attribute, regex: ^rel= } }
|
|
27
|
+
message: "A link with `target=\"_blank\"` gives the new page access to this one; add `rel=\"noreferrer\"`"
|
|
28
|
+
fix:
|
|
29
|
+
- insert-after: ' rel="noreferrer"'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
### JavaScript
|
|
35
|
+
|
|
36
|
+
#### Don't
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
const a = <a href={url} target="_blank">Docs</a>;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
#### Do
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const a = <a href={url} target="_blank" rel="noreferrer">Docs</a>;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### JSX
|
|
49
|
+
|
|
50
|
+
#### Don't
|
|
51
|
+
|
|
52
|
+
```jsx
|
|
53
|
+
const a = <a href={url} target="_blank">Docs</a>;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Do
|
|
57
|
+
|
|
58
|
+
```jsx
|
|
59
|
+
const a = <a href={url} target="_blank" rel="noreferrer">Docs</a>;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### TSX
|
|
63
|
+
|
|
64
|
+
#### Don't
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
const a = <a href={url} target="_blank">Docs</a>;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Do
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
const a = <a href={url} target="_blank" rel="noreferrer">Docs</a>;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Differences from the source
|
|
77
|
+
|
|
78
|
+
A link that already has a `rel` is not checked.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/jsx-no-undef
|
|
3
|
+
title: "Components used in JSX are declared"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the right import is the author's to choose"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/jsx-no-undef]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Every component used as a JSX tag is imported or declared.
|
|
13
|
+
|
|
14
|
+
**Why:** An undeclared component throws a ReferenceError when it renders.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
match:
|
|
18
|
+
kind: identifier
|
|
19
|
+
regex: ^[A-Z]
|
|
20
|
+
inside:
|
|
21
|
+
stopBy: neighbor
|
|
22
|
+
any: [{ kind: jsx_opening_element }, { kind: jsx_self_closing_element }]
|
|
23
|
+
field: name
|
|
24
|
+
where:
|
|
25
|
+
$$MATCH: undeclared
|
|
26
|
+
message: "`$$MATCH` is not defined; import or declare the component"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## How to fix
|
|
30
|
+
|
|
31
|
+
1. Import the component, or declare it, or correct the name.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
### JavaScript
|
|
36
|
+
|
|
37
|
+
#### Don't
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
export const App = () => <Header />;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### Do
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { Header } from "./header";
|
|
47
|
+
export const App = () => <Header />;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### JSX
|
|
51
|
+
|
|
52
|
+
#### Don't
|
|
53
|
+
|
|
54
|
+
```jsx
|
|
55
|
+
export const App = () => <Header />;
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Do
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
import { Header } from "./header";
|
|
62
|
+
export const App = () => <Header />;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### TSX
|
|
66
|
+
|
|
67
|
+
#### Don't
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
export const App = () => <Header />;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Do
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { Header } from "./header";
|
|
77
|
+
export const App = () => <Header />;
|
|
78
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-children-prop
|
|
3
|
+
title: "Children are passed as children, not as a prop"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "moving the children changes the element's shape by hand"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-children-prop]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Children go between an element's tags (or as the third argument of `createElement`), not in a
|
|
13
|
+
`children` prop.
|
|
14
|
+
|
|
15
|
+
**Why:** Passing `children` as a prop works, but hides the element's content where readers don't
|
|
16
|
+
look for it.
|
|
17
|
+
|
|
18
|
+
```codelaw
|
|
19
|
+
match:
|
|
20
|
+
any:
|
|
21
|
+
- kind: jsx_attribute
|
|
22
|
+
regex: ^children\b
|
|
23
|
+
- pattern: 'createElement($T, { children: $C, $$$REST })'
|
|
24
|
+
- pattern: 'React.createElement($T, { children: $C, $$$REST })'
|
|
25
|
+
message: "Pass children between the tags, not as the `children` prop"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## How to fix
|
|
29
|
+
|
|
30
|
+
1. Move the value between the opening and closing tags.
|
|
31
|
+
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
### JavaScript
|
|
35
|
+
|
|
36
|
+
#### Don't
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
const a = <Panel children={<p>Hi</p>} />;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
#### Do
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const a = <Panel>
|
|
46
|
+
<p>Hi</p>
|
|
47
|
+
</Panel>;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### JSX
|
|
51
|
+
|
|
52
|
+
#### Don't
|
|
53
|
+
|
|
54
|
+
```jsx
|
|
55
|
+
const a = <Panel children={<p>Hi</p>} />;
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Do
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
const a = <Panel>
|
|
62
|
+
<p>Hi</p>
|
|
63
|
+
</Panel>;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### TSX
|
|
67
|
+
|
|
68
|
+
#### Don't
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
const a = <Panel children={<p>Hi</p>} />;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Do
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
const a = <Panel>
|
|
78
|
+
<p>Hi</p>
|
|
79
|
+
</Panel>;
|
|
80
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-danger-with-children
|
|
3
|
+
title: "No dangerouslySetInnerHTML together with children"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which content the element should have needs the author"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-danger-with-children]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
An element with `dangerouslySetInnerHTML` has no children.
|
|
13
|
+
|
|
14
|
+
**Why:** React throws when an element has both, since it can't know which content wins.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
match:
|
|
18
|
+
any:
|
|
19
|
+
- kind: jsx_element
|
|
20
|
+
all:
|
|
21
|
+
- has: { field: open_tag, has: { stopBy: neighbor, kind: jsx_attribute, regex: ^dangerouslySetInnerHTML } }
|
|
22
|
+
- has:
|
|
23
|
+
stopBy: neighbor
|
|
24
|
+
any:
|
|
25
|
+
- kind: jsx_element
|
|
26
|
+
- kind: jsx_self_closing_element
|
|
27
|
+
- kind: jsx_expression
|
|
28
|
+
- kind: jsx_text
|
|
29
|
+
regex: \S
|
|
30
|
+
- kind: jsx_self_closing_element
|
|
31
|
+
all:
|
|
32
|
+
- has: { stopBy: neighbor, kind: jsx_attribute, regex: ^dangerouslySetInnerHTML }
|
|
33
|
+
- has: { stopBy: neighbor, kind: jsx_attribute, regex: ^children\b }
|
|
34
|
+
message: "An element can't have both `dangerouslySetInnerHTML` and children"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## How to fix
|
|
38
|
+
|
|
39
|
+
1. Keep either the HTML or the children, not both.
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### JavaScript
|
|
44
|
+
|
|
45
|
+
#### Don't
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
const a = <div dangerouslySetInnerHTML={{ __html: html }}>Fallback</div>;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Do
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const a = <div dangerouslySetInnerHTML={{ __html: html }} />;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### JSX
|
|
58
|
+
|
|
59
|
+
#### Don't
|
|
60
|
+
|
|
61
|
+
```jsx
|
|
62
|
+
const a = <div dangerouslySetInnerHTML={{ __html: html }}>Fallback</div>;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Do
|
|
66
|
+
|
|
67
|
+
```jsx
|
|
68
|
+
const a = <div dangerouslySetInnerHTML={{ __html: html }} />;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### TSX
|
|
72
|
+
|
|
73
|
+
#### Don't
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
const a = <div dangerouslySetInnerHTML={{ __html: html }}>Fallback</div>;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Do
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
const a = <div dangerouslySetInnerHTML={{ __html: html }} />;
|
|
83
|
+
```
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: react/no-deprecated
|
|
3
|
+
title: "No deprecated React APIs"
|
|
4
|
+
languages: [javascript, jsx, tsx]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "each deprecated API has its own replacement"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [eslint-plugin-react/no-deprecated]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Code doesn't use React APIs that are deprecated: the legacy `componentWill…` lifecycles,
|
|
13
|
+
`React.createClass`, `React.PropTypes`, `ReactDOM.render`, `hydrate` and `unmountComponentAtNode`.
|
|
14
|
+
|
|
15
|
+
**Why:** Deprecated APIs are removed in later versions, and the legacy lifecycles misbehave with
|
|
16
|
+
concurrent rendering.
|
|
17
|
+
|
|
18
|
+
```codelaw
|
|
19
|
+
match:
|
|
20
|
+
any:
|
|
21
|
+
- kind: method_definition
|
|
22
|
+
has: { field: name, regex: ^(componentWillMount|componentWillReceiveProps|componentWillUpdate)$ }
|
|
23
|
+
- pattern: React.createClass($$$A)
|
|
24
|
+
- pattern: React.PropTypes
|
|
25
|
+
- pattern: ReactDOM.render($$$A)
|
|
26
|
+
- pattern: ReactDOM.hydrate($$$A)
|
|
27
|
+
- pattern: ReactDOM.unmountComponentAtNode($$$A)
|
|
28
|
+
- pattern: ReactDOMServer.renderToNodeStream($$$A)
|
|
29
|
+
message: "This React API is deprecated and is removed in a later version"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## How to fix
|
|
33
|
+
|
|
34
|
+
1. Replace `ReactDOM.render` with `createRoot(el).render(…)` and `hydrate` with `hydrateRoot`.
|
|
35
|
+
2. Rename legacy lifecycles to their `UNSAFE_` names, or move their work to `componentDidMount`/`componentDidUpdate`.
|
|
36
|
+
3. Use the `prop-types` package instead of `React.PropTypes`.
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
### JavaScript
|
|
41
|
+
|
|
42
|
+
#### Don't
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
ReactDOM.render(<App />, root);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
#### Do
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
createRoot(root).render(<App />);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### JSX
|
|
55
|
+
|
|
56
|
+
#### Don't
|
|
57
|
+
|
|
58
|
+
```jsx
|
|
59
|
+
ReactDOM.render(<App />, root);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### Do
|
|
63
|
+
|
|
64
|
+
```jsx
|
|
65
|
+
createRoot(root).render(<App />);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### TSX
|
|
69
|
+
|
|
70
|
+
#### Don't
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
ReactDOM.render(<App />, root);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### Do
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
createRoot(root).render(<App />);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Differences from the source
|
|
83
|
+
|
|
84
|
+
The React version setting is not read: every listed API is reported.
|