-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic implementation to prevent refs usage
Add check for string usage in ref attributes Add unit tests Add docs Improve docs Improve tests Improve docs Add rule to index.js Fix linting errors Rename from no-refs to no-string-refs Update ESLint project readme Add titles
- Loading branch information
1 parent
712244f
commit f1c5560
Showing
5 changed files
with
249 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Prevent using string references (no-string-refs) | ||
|
||
Currently, two ways are supported by React to refer to components. The first one, providing a string identifier is considered legacy in the official documentation. Referring to components by setting an property on the `this` object in the reference callback is preferred. | ||
|
||
## Rule Details | ||
|
||
Invalid: | ||
|
||
```js | ||
var Hello = React.createClass({ | ||
render: function() { | ||
return <div ref="hello">Hello, world.</div>; | ||
} | ||
}); | ||
``` | ||
|
||
```js | ||
var Hello = React.createClass({ | ||
componentDidMount: function() { | ||
var component = this.refs.hello; | ||
// ...do something with component | ||
}, | ||
render: function() { | ||
return <div ref="hello">Hello, world.</div>; | ||
} | ||
}); | ||
``` | ||
|
||
Valid: | ||
|
||
```js | ||
var Hello = React.createClass({ | ||
componentDidMount: function() { | ||
var component = this.hello; | ||
// ...do something with component | ||
}, | ||
render() { | ||
return <div ref={c => this.hello = c}>Hello, world.</div>; | ||
} | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* @fileoverview Prevent string definitions for references and prevent referencing this.refs | ||
* @author Tom Hastjarjanto | ||
*/ | ||
'use strict'; | ||
|
||
var Components = require('../util/Components'); | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Rule Definition | ||
// ------------------------------------------------------------------------------ | ||
|
||
module.exports = Components.detect(function(context, components, utils) { | ||
/** | ||
* Checks if we are using refs | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {Boolean} True if we are using refs, false if not. | ||
*/ | ||
function isRefsUsage(node) { | ||
return Boolean( | ||
( | ||
utils.getParentES6Component() || | ||
utils.getParentES5Component() | ||
) && | ||
node.object.type === 'ThisExpression' && | ||
node.property.name === 'refs' | ||
); | ||
} | ||
|
||
/** | ||
* Checks if we are using a ref attribute | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {Boolean} True if we are using a ref attribute, false if not. | ||
*/ | ||
function isRefAttribute(node) { | ||
return Boolean( | ||
node.type === 'JSXAttribute' && | ||
node.name && | ||
node.name.name === 'ref' | ||
); | ||
} | ||
|
||
/** | ||
* Checks if a node contains a string value | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {Boolean} True if the node contains a string value, false if not. | ||
*/ | ||
function containsStringLiteral(node) { | ||
return Boolean( | ||
node.value && | ||
node.value.type === 'Literal' && | ||
typeof node.value.value === 'string' | ||
); | ||
} | ||
|
||
/** | ||
* Checks if a node contains a string value within a jsx expression | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {Boolean} True if the node contains a string value within a jsx expression, false if not. | ||
*/ | ||
function containsStringExpressionContainer(node) { | ||
return Boolean( | ||
node.value && | ||
node.value.type === 'JSXExpressionContainer' && | ||
node.value.expression && | ||
node.value.expression.type === 'Literal' && | ||
typeof node.value.expression.value === 'string' | ||
); | ||
} | ||
|
||
return { | ||
MemberExpression: function(node) { | ||
if (isRefsUsage(node)) { | ||
context.report(node, 'Using this.refs is deprecated.'); | ||
} | ||
}, | ||
JSXAttribute: function(node) { | ||
if ( | ||
isRefAttribute(node) && | ||
(containsStringLiteral(node) || containsStringExpressionContainer(node)) | ||
) { | ||
context.report(node, 'Using string literals in ref attributes is deprecated.'); | ||
} | ||
} | ||
}; | ||
}); | ||
|
||
module.exports.schema = []; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/** | ||
* @fileoverview Prevent string definitions for references and prevent referencing this.refs | ||
* @author Tom Hastjarjanto | ||
*/ | ||
'use strict'; | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Requirements | ||
// ------------------------------------------------------------------------------ | ||
|
||
var rule = require('../../../lib/rules/no-string-refs'); | ||
var RuleTester = require('eslint').RuleTester; | ||
|
||
require('babel-eslint'); | ||
|
||
// ------------------------------------------------------------------------------ | ||
// Tests | ||
// ------------------------------------------------------------------------------ | ||
|
||
var ruleTester = new RuleTester(); | ||
ruleTester.run('no-refs', rule, { | ||
|
||
valid: [{ | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' componentDidMount: function() {', | ||
' var component = this.hello;', | ||
' },', | ||
' render: function() {', | ||
' return <div ref={c => this.hello = c}>Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parser: 'babel-eslint', | ||
ecmaFeatures: { | ||
jsx: true | ||
} | ||
} | ||
], | ||
|
||
invalid: [{ | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' componentDidMount: function() {', | ||
' var component = this.refs.hello;', | ||
' },', | ||
' render: function() {', | ||
' return <div>Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parser: 'babel-eslint', | ||
ecmaFeatures: { | ||
classes: true, | ||
jsx: true | ||
}, | ||
errors: [{ | ||
message: 'Using this.refs is deprecated.' | ||
}] | ||
}, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' return <div ref="hello">Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parser: 'babel-eslint', | ||
ecmaFeatures: { | ||
classes: true, | ||
jsx: true | ||
}, | ||
errors: [{ | ||
message: 'Using string literals in ref attributes is deprecated.' | ||
}] | ||
}, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' render: function() {', | ||
' return <div ref={\'hello\'}>Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parser: 'babel-eslint', | ||
ecmaFeatures: { | ||
classes: true, | ||
jsx: true | ||
}, | ||
errors: [{ | ||
message: 'Using string literals in ref attributes is deprecated.' | ||
}] | ||
}, { | ||
code: [ | ||
'var Hello = React.createClass({', | ||
' componentDidMount: function() {', | ||
' var component = this.refs.hello;', | ||
' },', | ||
' render: function() {', | ||
' return <div ref="hello">Hello {this.props.name}</div>;', | ||
' }', | ||
'});' | ||
].join('\n'), | ||
parser: 'babel-eslint', | ||
ecmaFeatures: { | ||
classes: true, | ||
jsx: true | ||
}, | ||
errors: [{ | ||
message: 'Using this.refs is deprecated.' | ||
}, { | ||
message: 'Using string literals in ref attributes is deprecated.' | ||
}] | ||
} | ||
]}); |