Skip to content

Commit

Permalink
Merge pull request #1989 from sergei-startsev/fix-1967-no-this-in-sfc
Browse files Browse the repository at this point in the history
Fix `no-this-in-sfc` rule behavior for arrow functions inside a class field
  • Loading branch information
ljharb authored Sep 20, 2018
2 parents 60b5642 + e0d5821 commit a93080e
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 5 deletions.
8 changes: 4 additions & 4 deletions lib/rules/no-this-in-sfc.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ module.exports = {

create: Components.detect((context, components, utils) => ({
MemberExpression(node) {
const component = components.get(utils.getParentStatelessComponent());
if (!component) {
return;
}
if (node.object.type === 'ThisExpression') {
const component = components.get(utils.getParentStatelessComponent());
if (!component) {
return;
}
context.report({
node: node,
message: ERROR_MESSAGE
Expand Down
25 changes: 24 additions & 1 deletion lib/util/Components.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,13 @@ function componentRule(rule, context) {
const node = scope.block;
const isClass = node.type === 'ClassExpression';
const isFunction = /Function/.test(node.type); // Functions
const isMethod = node.parent && node.parent.type === 'MethodDefinition'; // Classes methods
const isArrowFunction = node.type === 'ArrowFunctionExpression';
let functionScope = scope;
if (isArrowFunction) {
functionScope = utils.getParentFunctionScope(scope);
}
const methodNode = functionScope && functionScope.block.parent;
const isMethod = methodNode && methodNode.type === 'MethodDefinition'; // Classes methods
const isArgument = node.parent && node.parent.type === 'CallExpression'; // Arguments (callback, etc.)
// Attribute Expressions inside JSX Elements (<button onClick={() => props.handleClick()}></button>)
const isJSXExpressionContainer = node.parent && node.parent.type === 'JSXExpressionContainer';
Expand All @@ -486,6 +492,23 @@ function componentRule(rule, context) {
return null;
},

/**
* Get a parent scope created by a FunctionExpression or FunctionDeclaration
* @param {Scope} scope The child scope
* @returns {Scope} A parent function scope
*/
getParentFunctionScope(scope) {
scope = scope.upper;
while (scope) {
const type = scope.block.type;
if (type === 'FunctionExpression' || type === 'FunctionDeclaration') {
return scope;
}
scope = scope.upper;
}
return null;
},

/**
* Get the related component from a node
*
Expand Down
40 changes: 40 additions & 0 deletions tests/lib/rules/no-this-in-sfc.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ ruleTester.run('no-this-in-sfc', rule, {
code: 'const Foo = (props) => props.foo ? <span>{props.bar}</span> : null;'
}, {
code: 'const Foo = ({ foo, bar }) => foo ? <span>{bar}</span> : null;'
}, {
code: `
class Foo {
bar() {
() => {
this.something();
return null;
};
}
}`
}, {
code: `
class Foo {
bar() {
() => () => {
this.something();
return null;
};
}
}`
}],
invalid: [{
code: `
Expand Down Expand Up @@ -165,5 +185,25 @@ ruleTester.run('no-this-in-sfc', rule, {
return <div onClick={onClick}>{this.props.foo}</div>;
}`,
errors: [{message: ERROR_MESSAGE}, {message: ERROR_MESSAGE}]
}, {
code: `
() => {
this.something();
return null;
}`,
errors: [{message: ERROR_MESSAGE}]
}, {
code: `
class Foo {
bar() {
function Bar(){
return () => {
this.something();
return null;
}
}
}
}`,
errors: [{message: ERROR_MESSAGE}]
}]
});

0 comments on commit a93080e

Please sign in to comment.