Skip to content

Commit 6e88b2a

Browse files
committed
Make no-unused-state be able to handle arrow function
1 parent 1f14fad commit 6e88b2a

File tree

2 files changed

+113
-19
lines changed

2 files changed

+113
-19
lines changed

lib/rules/no-unused-state.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ function getInitialClassInfo() {
5151

5252
// Names of local variables that may be pointing to this.state. To
5353
// track this properly, we would need to keep track of all locals,
54-
// shadowing, assignments, etc. To keep things simple, we only
55-
// maintain one set of aliases per method and accept that it will
56-
// produce some false negatives.
57-
aliases: null
54+
// shadowing, assignments, etc. To keep things simple, we clear the
55+
// current set of aliases after each method definition and accept
56+
// that it will produce some false negatives.
57+
aliases: new Set()
5858
};
5959
}
6060

@@ -86,7 +86,6 @@ module.exports = {
8686

8787
const isAliasedStateReference =
8888
node.type === 'Identifier' &&
89-
classInfo.aliases &&
9089
classInfo.aliases.has(node.name);
9190

9291
return isDirectStateReference || isAliasedStateReference;
@@ -125,10 +124,7 @@ module.exports = {
125124
for (const prop of node.properties) {
126125
if (prop.type === 'Property') {
127126
addUsedStateField(prop.key);
128-
} else if (
129-
prop.type === 'ExperimentalRestProperty' &&
130-
classInfo.aliases
131-
) {
127+
} else if (prop.type === 'ExperimentalRestProperty') {
132128
classInfo.aliases.add(getName(prop.argument));
133129
}
134130
}
@@ -139,14 +135,14 @@ module.exports = {
139135
function handleAssignment(left, right) {
140136
switch (left.type) {
141137
case 'Identifier':
142-
if (isStateReference(right) && classInfo.aliases) {
138+
if (isStateReference(right)) {
143139
classInfo.aliases.add(left.name);
144140
}
145141
break;
146142
case 'ObjectPattern':
147143
if (isStateReference(right)) {
148144
handleStateDestructuring(left);
149-
} else if (isThisExpression(right) && classInfo.aliases) {
145+
} else if (isThisExpression(right)) {
150146
for (const prop of left.properties) {
151147
if (prop.type === 'Property' && getName(prop.key) === 'state') {
152148
const name = getName(prop.value);
@@ -237,6 +233,15 @@ module.exports = {
237233
) {
238234
addStateFields(node.value);
239235
}
236+
237+
if (
238+
!node.static &&
239+
node.value &&
240+
node.value.type === 'ArrowFunctionExpression'
241+
) {
242+
// Create a new set for this.state aliases local to this method.
243+
classInfo.aliases = new Set();
244+
}
240245
},
241246

242247
MethodDefinition() {
@@ -247,14 +252,6 @@ module.exports = {
247252
classInfo.aliases = new Set();
248253
},
249254

250-
'MethodDefinition:exit'() {
251-
if (!classInfo) {
252-
return;
253-
}
254-
// Forget our set of local aliases.
255-
classInfo.aliases = null;
256-
},
257-
258255
FunctionExpression(node) {
259256
if (!classInfo) {
260257
return;

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

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,22 @@ eslintTester.run('no-unused-state', rule, {
387387
return <SomeComponent foo={foo} />;
388388
}
389389
}`,
390+
`class NonRenderClassMethodFalseNegativeTest extends React.Component {
391+
constructor() {
392+
this.state = { foo: 0, bar: 0 };
393+
}
394+
doSomething() {
395+
const { foo } = this.state;
396+
return this.state.foo;
397+
}
398+
doSomethingElse() {
399+
const { state: { bar }} = this;
400+
return bar;
401+
}
402+
render() {
403+
return <SomeComponent />;
404+
}
405+
}`,
390406
{
391407
code: `class TypeCastExpressionSpreadFalseNegativeTest extends React.Component {
392408
constructor() {
@@ -397,6 +413,72 @@ eslintTester.run('no-unused-state', rule, {
397413
}
398414
}`,
399415
parser: 'babel-eslint'
416+
},
417+
{
418+
code: `class ArrowFunctionClassMethodDestructuringFalseNegativeTest extends React.Component {
419+
constructor() {
420+
this.state = { foo: 0 };
421+
}
422+
423+
doSomething = () => {
424+
const { state: { foo } } = this;
425+
426+
return foo;
427+
}
428+
429+
render() {
430+
return <SomeComponent />;
431+
}
432+
}`,
433+
parser: 'babel-eslint'
434+
},
435+
{
436+
code: `class ArrowFunctionClassMethodWithClassPropertyTransformFalseNegativeTest extends React.Component {
437+
state = { foo: 0 };
438+
439+
doSomething = () => {
440+
const { state:{ foo } } = this;
441+
442+
return foo;
443+
}
444+
445+
render() {
446+
return <SomeComponent />;
447+
}
448+
}`,
449+
parser: 'babel-eslint'
450+
},
451+
{
452+
code: `class ArrowFunctionClassMethodDeepDestructuringFalseNegativeTest extends React.Component {
453+
state = { foo: { bar: 0 } };
454+
455+
doSomething = () => {
456+
const { state: { foo: { bar }}} = this;
457+
458+
return bar;
459+
}
460+
461+
render() {
462+
return <SomeComponent />;
463+
}
464+
}`,
465+
parser: 'babel-eslint'
466+
},
467+
{
468+
code: `class ArrowFunctionClassMethodDestructuringAssignmentFalseNegativeTest extends React.Component {
469+
state = { foo: 0 };
470+
471+
doSomething = () => {
472+
const { state: { foo: bar }} = this;
473+
474+
return bar;
475+
}
476+
477+
render() {
478+
return <SomeComponent />;
479+
}
480+
}`,
481+
parser: 'babel-eslint'
400482
}
401483
],
402484

@@ -642,6 +724,21 @@ eslintTester.run('no-unused-state', rule, {
642724
}`,
643725
errors: getErrorMessages(['foo'])
644726
},
727+
{
728+
code: `class UnusedStateArrowFunctionMethodTest extends React.Component {
729+
constructor() {
730+
this.state = { foo: 0 };
731+
}
732+
doSomething = () => {
733+
return null;
734+
}
735+
render() {
736+
return <SomeComponent />;
737+
}
738+
}`,
739+
errors: getErrorMessages(['foo']),
740+
parser: 'babel-eslint'
741+
},
645742
{
646743
code: `class TypeCastExpressionTest extends React.Component {
647744
constructor() {

0 commit comments

Comments
 (0)