diff --git a/docs/rules/no-unused-state.md b/docs/rules/no-unused-state.md index 7a2f3b44c5..51d19e32de 100644 --- a/docs/rules/no-unused-state.md +++ b/docs/rules/no-unused-state.md @@ -45,3 +45,16 @@ var UnusedGetInitialStateTest = createReactClass({ } }) ``` + +## Rule Options + +This rule can take one argument to ignore some specific states during validation. + +```js +... +"react/no-unused-state": [, { ignore: }] +... +``` + +- `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0. +- `ignore`: optional array of states name to ignore during validation. diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js index c1986755c4..71dde7f5b2 100644 --- a/lib/rules/no-unused-state.js +++ b/lib/rules/no-unused-state.js @@ -90,10 +90,34 @@ module.exports = { messages, - schema: [], + schema: [{ + type: 'object', + properties: { + ignore: { + type: 'array', + items: { + type: 'string', + }, + uniqueItems: true, + }, + }, + additionalProperties: false, + }], }, create(context) { + const defaults = { skipShapeProps: true, customValidators: [], ignore: [] }; + const configuration = Object.assign({}, defaults, context.options[0] || {}); + + /** + * Checks if the state is ignored + * @param {string} name Name of the state to check. + * @returns {boolean} True if the state is ignored, false if not. + */ + function isIgnored(name) { + return configuration.ignore.indexOf(name) !== -1; + } + // Non-null when we are inside a React component ClassDeclaration and we have // not yet encountered any use of this.state which we have chosen not to // analyze. If we encounter any such usage (like this.state being spread as @@ -223,7 +247,7 @@ module.exports = { // Report all unused state fields. classInfo.stateFields.forEach((node) => { const name = getName(node.key); - if (!classInfo.usedStateFields.has(name)) { + if (!classInfo.usedStateFields.has(name) && !isIgnored(name)) { report(context, messages.unusedStateField, 'unusedStateField', { node, data: { diff --git a/tests/lib/rules/no-unused-state.js b/tests/lib/rules/no-unused-state.js index e08197e589..e1209e4f7f 100644 --- a/tests/lib/rules/no-unused-state.js +++ b/tests/lib/rules/no-unused-state.js @@ -1110,6 +1110,58 @@ eslintTester.run('no-unused-state', rule, { } `, features: ['types', 'class fields'], + }, + { + code: ` + var UnusedGetInitialStateTest = createReactClass({ + getInitialState: function() { + return { foo: 0 }; + }, + render: function() { + return ; + } + }) + `, + options: [{ ignore: ['foo'] }], + }, + { + code: ` + var UnusedComputedStringLiteralKeyStateTest = createReactClass({ + getInitialState: function() { + return { ['foo']: 0 }; + }, + render: function() { + return ; + } + }) + `, + options: [{ ignore: ['foo'] }], + }, + { + code: ` + class UnusedCtorStateTest extends React.Component { + constructor() { + this.state = { foo: 0 }; + } + render() { + return ; + } + } + `, + options: [{ ignore: ['foo'] }], + }, + { + code: ` + class UnusedCtorStateTest extends React.Component { + constructor() { + this.state = { ['foo']: 0 }; + } + render() { + return ; + } + } + `, + options: [{ ignore: ['foo'] }], } )), @@ -1616,5 +1668,61 @@ eslintTester.run('no-unused-state', rule, { 'thisDestructStateDestructPropUnused', ]), }, + { + code: ` + var UnusedGetInitialStateTest = createReactClass({ + getInitialState: function() { + return { foo: 0, dummy: 0 }; + }, + render: function() { + return ; + } + }) + `, + options: [{ ignore: ['foo'] }], + errors: getErrorMessages(['dummy']), + }, + { + code: ` + var UnusedGetInitialStateTest = createReactClass({ + getInitialState: function() { + return { ['foo']: 0, ['dummy']: 0 }; + }, + render: function() { + return ; + } + }) + `, + options: [{ ignore: ['foo'] }], + errors: getErrorMessages(['dummy']), + }, + { + code: ` + class UnusedCtorStateTest extends React.Component { + constructor() { + this.state = { foo: 0, dummy: 0 }; + } + render() { + return ; + } + } + `, + options: [{ ignore: ['foo'] }], + errors: getErrorMessages(['dummy']), + }, + { + code: ` + class UnusedCtorStateTest extends React.Component { + constructor() { + this.state = { ['foo']: 0, ['dummy']: 0 }; + } + render() { + return ; + } + } + `, + options: [{ ignore: ['foo'] }], + errors: getErrorMessages(['dummy']), + }, ]), });