Skip to content

Commit df2cb36

Browse files
committed
[New] no-unused-state: Support ignore option
1 parent e6b5b41 commit df2cb36

File tree

3 files changed

+147
-2
lines changed

3 files changed

+147
-2
lines changed

docs/rules/no-unused-state.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,16 @@ var UnusedGetInitialStateTest = createReactClass({
4545
}
4646
})
4747
```
48+
49+
## Rule Options
50+
51+
This rule can take one argument to ignore some specific states during validation.
52+
53+
```js
54+
...
55+
"react/no-unused-state": [<enabled>, { ignore: <ignore> }]
56+
...
57+
```
58+
59+
- `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
60+
- `ignore`: optional array of states name to ignore during validation.

lib/rules/no-unused-state.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,34 @@ module.exports = {
9090

9191
messages,
9292

93-
schema: [],
93+
schema: [{
94+
type: 'object',
95+
properties: {
96+
ignore: {
97+
type: 'array',
98+
items: {
99+
type: 'string',
100+
},
101+
uniqueItems: true,
102+
},
103+
},
104+
additionalProperties: false,
105+
}],
94106
},
95107

96108
create(context) {
109+
const defaults = { skipShapeProps: true, customValidators: [], ignore: [] };
110+
const configuration = Object.assign({}, defaults, context.options[0] || {});
111+
112+
/**
113+
* Checks if the state is ignored
114+
* @param {string} name Name of the state to check.
115+
* @returns {boolean} True if the state is ignored, false if not.
116+
*/
117+
function isIgnored(name) {
118+
return configuration.ignore.indexOf(name) !== -1;
119+
}
120+
97121
// Non-null when we are inside a React component ClassDeclaration and we have
98122
// not yet encountered any use of this.state which we have chosen not to
99123
// analyze. If we encounter any such usage (like this.state being spread as
@@ -223,7 +247,7 @@ module.exports = {
223247
// Report all unused state fields.
224248
classInfo.stateFields.forEach((node) => {
225249
const name = getName(node.key);
226-
if (!classInfo.usedStateFields.has(name)) {
250+
if (!classInfo.usedStateFields.has(name) && !isIgnored(name)) {
227251
report(context, messages.unusedStateField, 'unusedStateField', {
228252
node,
229253
data: {

tests/lib/rules/no-unused-state.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,58 @@ eslintTester.run('no-unused-state', rule, {
11101110
}
11111111
`,
11121112
features: ['types', 'class fields'],
1113+
},
1114+
{
1115+
code: `
1116+
var UnusedGetInitialStateTest = createReactClass({
1117+
getInitialState: function() {
1118+
return { foo: 0 };
1119+
},
1120+
render: function() {
1121+
return <SomeComponent />;
1122+
}
1123+
})
1124+
`,
1125+
options: [{ ignore: ['foo'] }],
1126+
},
1127+
{
1128+
code: `
1129+
var UnusedComputedStringLiteralKeyStateTest = createReactClass({
1130+
getInitialState: function() {
1131+
return { ['foo']: 0 };
1132+
},
1133+
render: function() {
1134+
return <SomeComponent />;
1135+
}
1136+
})
1137+
`,
1138+
options: [{ ignore: ['foo'] }],
1139+
},
1140+
{
1141+
code: `
1142+
class UnusedCtorStateTest extends React.Component {
1143+
constructor() {
1144+
this.state = { foo: 0 };
1145+
}
1146+
render() {
1147+
return <SomeComponent />;
1148+
}
1149+
}
1150+
`,
1151+
options: [{ ignore: ['foo'] }],
1152+
},
1153+
{
1154+
code: `
1155+
class UnusedCtorStateTest extends React.Component {
1156+
constructor() {
1157+
this.state = { ['foo']: 0 };
1158+
}
1159+
render() {
1160+
return <SomeComponent />;
1161+
}
1162+
}
1163+
`,
1164+
options: [{ ignore: ['foo'] }],
11131165
}
11141166
)),
11151167

@@ -1616,5 +1668,61 @@ eslintTester.run('no-unused-state', rule, {
16161668
'thisDestructStateDestructPropUnused',
16171669
]),
16181670
},
1671+
{
1672+
code: `
1673+
var UnusedGetInitialStateTest = createReactClass({
1674+
getInitialState: function() {
1675+
return { foo: 0, dummy: 0 };
1676+
},
1677+
render: function() {
1678+
return <SomeComponent />;
1679+
}
1680+
})
1681+
`,
1682+
options: [{ ignore: ['foo'] }],
1683+
errors: getErrorMessages(['dummy']),
1684+
},
1685+
{
1686+
code: `
1687+
var UnusedGetInitialStateTest = createReactClass({
1688+
getInitialState: function() {
1689+
return { ['foo']: 0, ['dummy']: 0 };
1690+
},
1691+
render: function() {
1692+
return <SomeComponent />;
1693+
}
1694+
})
1695+
`,
1696+
options: [{ ignore: ['foo'] }],
1697+
errors: getErrorMessages(['dummy']),
1698+
},
1699+
{
1700+
code: `
1701+
class UnusedCtorStateTest extends React.Component {
1702+
constructor() {
1703+
this.state = { foo: 0, dummy: 0 };
1704+
}
1705+
render() {
1706+
return <SomeComponent />;
1707+
}
1708+
}
1709+
`,
1710+
options: [{ ignore: ['foo'] }],
1711+
errors: getErrorMessages(['dummy']),
1712+
},
1713+
{
1714+
code: `
1715+
class UnusedCtorStateTest extends React.Component {
1716+
constructor() {
1717+
this.state = { ['foo']: 0, ['dummy']: 0 };
1718+
}
1719+
render() {
1720+
return <SomeComponent />;
1721+
}
1722+
}
1723+
`,
1724+
options: [{ ignore: ['foo'] }],
1725+
errors: getErrorMessages(['dummy']),
1726+
},
16191727
]),
16201728
});

0 commit comments

Comments
 (0)