Repository: jsx-eslint/eslint-plugin-react (JavaScript)

Issue:
Getting unrelated (react/no-this-in-sfc) violation error
I ran the following simple js program. But I got an error at line this.getA(); which is totally unrelated here. The error said "stateless functional components should not use this (react/no-this-in-sfc)"

```
class Demo {
    getA = () => 'a';

    hasBug = () => {
        this.getA();

        return null;
    };
}
```

What did you expect to happen?
No error expected

What actually happened? Please include the actual, raw output from ESLint.
Got this error - stateless functional components should not use this (react/no-this-in-sfc) for line  ```this.getA()```


Discussion:
Component detection is probably recognizing it as an SFC since it returns null; this seems like a case where we should have less confidence because a) it's named not in PascalCase, b) it's a class field.

cc @alexzherdev 
We're bitten again by the fact that `Components.get` can return something we're not entirely sure is a component (confidence = 1).
I can make a local fix in this rule to make sure this doesn't happen. But making `Components.get` always require confidence of 2 breaks 37 different tests in the suite 😕 
@alexzherdev in this case it should be reporting a lower confidence than 1, because it's a class field, and because it's not named in PascalCase.
Not sure if this is due to the same reason.

Another sample causing unnecessary error:
```jsx
import React from 'react';

class Extended extends Base {
  testA = () => {
    this.abc(); // Error here - react/no-this-in-sfc

    return (
      <div>
        Hello World
      </div>
    );
  }

  testB() {
    this.abc(); // No error here

    return (
      <div>
        Hello World
      </div>
    );
  }
}

```

It doesn't happen inside non-arrow functions




Patch:
diff --git a/lib/util/Components.js b/lib/util/Components.js
index b7e2a99a63..15026089dc 100644
--- a/lib/util/Components.js
+++ b/lib/util/Components.js
@@ -456,15 +456,12 @@ function componentRule(rule, context) {
       let scope = context.getScope();
       while (scope) {
         const node = scope.block;
-        const isClass = node.type === 'ClassExpression';
         const isFunction = /Function/.test(node.type); // Functions
-        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 isArrowFunction = astUtil.isArrowFunction(node);
+        const enclosingScope = isArrowFunction ? utils.getArrowFunctionScope(scope) : scope;
+        const enclosingScopeParent = enclosingScope && enclosingScope.block.parent;
+        const isClass = enclosingScope && astUtil.isClass(enclosingScope.block);
+        const isMethod = enclosingScopeParent && enclosingScopeParent.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';
@@ -482,15 +479,14 @@ function componentRule(rule, context) {
     },
 
     /**
-     * Get a parent scope created by a FunctionExpression or FunctionDeclaration
-     * @param {Scope} scope The child scope
-     * @returns {Scope} A parent function scope
+     * Get an enclosing scope used to find `this` value by an arrow function
+     * @param {Scope} scope Current scope
+     * @returns {Scope} An enclosing scope used by an arrow function
      */
-    getParentFunctionScope(scope) {
+    getArrowFunctionScope(scope) {
       scope = scope.upper;
       while (scope) {
-        const type = scope.block.type;
-        if (type === 'FunctionExpression' || type === 'FunctionDeclaration') {
+        if (astUtil.isFunction(scope.block) || astUtil.isClass(scope.block)) {
           return scope;
         }
         scope = scope.upper;
diff --git a/lib/util/ast.js b/lib/util/ast.js
index 10fc434625..9af7e0c5ce 100644
--- a/lib/util/ast.js
+++ b/lib/util/ast.js
@@ -102,11 +102,41 @@ function isFunctionLikeExpression(node) {
   return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';
 }
 
+/**
+ * Checks if the node is a function.
+ * @param {Object} context The node to check
+ * @return {Boolean} true if it's a function
+ */
+function isFunction(node) {
+  return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration';
+}
+
+/**
+ * Checks if the node is an arrow function.
+ * @param {Object} context The node to check
+ * @return {Boolean} true if it's an arrow function
+ */
+function isArrowFunction(node) {
+  return node.type === 'ArrowFunctionExpression';
+}
+
+/**
+ * Checks if the node is a class.
+ * @param {Object} context The node to check
+ * @return {Boolean} true if it's a class
+ */
+function isClass(node) {
+  return node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
+}
+
 module.exports = {
   findReturnStatement: findReturnStatement,
   getPropertyName: getPropertyName,
   getPropertyNameNode: getPropertyNameNode,
   getComponentProperties: getComponentProperties,
-  isNodeFirstInLine: isNodeFirstInLine,
-  isFunctionLikeExpression: isFunctionLikeExpression
+  isArrowFunction: isArrowFunction,
+  isClass: isClass,
+  isFunction: isFunction,
+  isFunctionLikeExpression: isFunctionLikeExpression,
+  isNodeFirstInLine: isNodeFirstInLine
 };


Test patch:
diff --git a/tests/lib/rules/no-this-in-sfc.js b/tests/lib/rules/no-this-in-sfc.js
index 63a849f840..b5d87f0267 100644
--- a/tests/lib/rules/no-this-in-sfc.js
+++ b/tests/lib/rules/no-this-in-sfc.js
@@ -119,6 +119,24 @@ ruleTester.run('no-this-in-sfc', rule, {
         };
       }
     }`
+  }, {
+    code: `
+    class Foo {
+      bar = () => {
+        this.something();
+        return null;
+      };
+    }`,
+    parser: 'babel-eslint'
+  }, {
+    code: `
+    class Foo {
+      bar = () => () => {
+        this.something();
+        return null;
+      };
+    }`,
+    parser: 'babel-eslint'
   }],
   invalid: [{
     code: `