Repository: yannickcr/eslint-plugin-react
Pull request #1755 (2018-04-01 23:12:58)

Add jsx-props-no-multi-spaces rule
This rule enforced consistent spacing of JSX tag attributes and is especially useful together with `jsx-indent-props` in "first" mode (#1729) to actually require consistency of where the first attribute begins.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32d9e1ab..14e1dcbb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,57 +3,6 @@ All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
 
-## [7.8.2] - 2018-05-13
-### Fixed
-* Fix crash in [`boolean-prop-naming`][] when encountering a required shape prop type ([#1791][] @pcorpet)
-
-[7.8.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v7.8.1...v7.8.2
-[#1791]: https://github.com/yannickcr/eslint-plugin-react/issues/1791
-
-## [7.8.1] - 2018-05-12
-### Fixed
-* Fix crash in [`no-deprecated`][] when encountering a class constructor ([#1785][] @taddei)
-
-[7.8.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v7.8.0...v7.8.1
-[#1785]: https://github.com/yannickcr/eslint-plugin-react/issues/1785
-
-## [7.8.0] - 2018-05-11
-### Added
-* Add support for fragments to [`react-in-jsx-scope`][] ([#1758][])
-* Add support for Flow generic PropType to [`require-default-props`][] ([#1724][] @Miziak)
-* Add component whitelist option to [`forbid-component-props`][] ([#1732][] @ThiefMaster)
-* Add support for React 16.3 lifecycle methods to [`no-unused-prop-types`][] ([#1681][] @bvaughn)
-* Add support for React 16.3 lifecycle methods to [`sort-comp`][] ([#1767][] @joe-denea)
-* Add support for React 16.3 lifecycle methods to [`no-typos`][]
-* Add support for `prevState` and `nextState` to [`no-unused-state`][] ([#1759][])
-* Add warnings for `componentWillMount`, `componentWillReceiveProps` and `componentWillUpdate` lifecycle methods in [`no-deprecated`][] ([#1750][] @sergei-startsev)
-
-### Fixed
-* Fix [`no-typos`][] false positive on custom `PropType` classes ([#1389][] @brettdh)
-* Fix [`boolean-prop-naming`][] to handle required props ([#1389][] @louisscruz)
-* Fix [`jsx-curly-brace-presence`][] to allow whitespace JSX container ([#1717][] @sharmilajesupaul)
-* Fix [`jsx-no-bind`][] to handle ternary conditions ([#1722][] @gwenaellarmet)
-
-### Changed
-* Documentation improvements ([#1699][] @ronanmathew, [#1743][] @ybiquitous, [#1753][] @awthwathje, [#1783][] @chentsulin, [#1703][] @ferhatelmas)
-
-[7.8.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v7.7.0...v7.8.0
-[#1758]: https://github.com/yannickcr/eslint-plugin-react/issues/1758
-[#1724]: https://github.com/yannickcr/eslint-plugin-react/issues/1724
-[#1732]: https://github.com/yannickcr/eslint-plugin-react/issues/1732
-[#1681]: https://github.com/yannickcr/eslint-plugin-react/pull/1681
-[#1767]: https://github.com/yannickcr/eslint-plugin-react/issues/1767
-[#1759]: https://github.com/yannickcr/eslint-plugin-react/issues/1759
-[#1750]: https://github.com/yannickcr/eslint-plugin-react/pull/1750
-[#1389]: https://github.com/yannickcr/eslint-plugin-react/issues/1389
-[#1717]: https://github.com/yannickcr/eslint-plugin-react/issues/1717
-[#1722]: https://github.com/yannickcr/eslint-plugin-react/issues/1722
-[#1699]: https://github.com/yannickcr/eslint-plugin-react/pull/1699
-[#1743]: https://github.com/yannickcr/eslint-plugin-react/pull/1743
-[#1753]: https://github.com/yannickcr/eslint-plugin-react/issues/1753
-[#1783]: https://github.com/yannickcr/eslint-plugin-react/pull/1783
-[#1703]: https://github.com/yannickcr/eslint-plugin-react/pull/1703
-
 ## [7.7.0] - 2018-02-19
 ### Added
 * [`forbid-foreign-prop-types`][]: add `allowInPropTypes` option ([#1655][] @iansu)
@@ -81,7 +30,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 [#1610]: https://github.com/yannickcr/eslint-plugin-react/pull/1610
 [#1414]: https://github.com/yannickcr/eslint-plugin-react/pull/1414
 [#1260]: https://github.com/yannickcr/eslint-plugin-react/pull/1260
-[#1571]: https://github.com/yannickcr/eslint-plugin-react/issues/1571
+[#1571]: https://github.com/yannickcr/eslint-plugin-react/issue/1571
 
 ## [7.6.1] - 2018-01-28
 ### Fixed
@@ -91,7 +40,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 
 [7.6.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v7.6.0...v7.6.1
 [#1659]: https://github.com/yannickcr/eslint-plugin-react/pull/1659
-[#1657]: https://github.com/yannickcr/eslint-plugin-react/issues/1657
+[#1657]: https://github.com/yannickcr/eslint-plugin-react/issue/1657
 [#1653]: https://github.com/yannickcr/eslint-plugin-react/pull/1653
 
 ## [7.6.0] - 2018-01-25
diff --git a/README.md b/README.md
index 865d2cbd..88bf1d05 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ Enable the rules that you would like to use.
 * [react/no-children-prop](docs/rules/no-children-prop.md): Prevent passing children as props
 * [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
 * [react/no-danger-with-children](docs/rules/no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML
-* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods, including component lifecyle methods
+* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
 * [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
 * [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
 * [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state`
@@ -158,6 +158,7 @@ Enable the rules that you would like to use.
 * [react/jsx-one-expression-per-line](docs/rules/jsx-one-expression-per-line.md): Limit to one expression per line in JSX
 * [react/jsx-curly-brace-presence](docs/rules/jsx-curly-brace-presence.md): Enforce curly braces or disallow unnecessary curly braces in JSX
 * [react/jsx-pascal-case](docs/rules/jsx-pascal-case.md): Enforce PascalCase for user-defined JSX components
+* [react/jsx-props-no-multi-spaces](docs/rules/jsx-props-no-multi-spaces.md): Disallow multiple spaces between inline JSX props (fixable)
 * [react/jsx-sort-default-props](docs/rules/jsx-sort-default-props.md): Enforce default props alphabetical sorting
 * [react/jsx-sort-props](docs/rules/jsx-sort-props.md): Enforce props alphabetical sorting (fixable)
 * [react/jsx-space-before-closing](docs/rules/jsx-space-before-closing.md): Validate spacing before closing bracket in JSX (fixable)
@@ -248,7 +249,7 @@ ESLint-plugin-React is licensed under the [MIT License](http://www.opensource.or
 [coverage-image]: https://img.shields.io/coveralls/yannickcr/eslint-plugin-react/master.svg
 
 [climate-url]: https://codeclimate.com/github/yannickcr/eslint-plugin-react
-[climate-image]: https://img.shields.io/codeclimate/maintainability/yannickcr/eslint-plugin-react.svg
+[climate-image]: https://img.shields.io/codeclimate/github/yannickcr/eslint-plugin-react.svg
 
 [status-url]: https://github.com/yannickcr/eslint-plugin-react/pulse
-[status-image]: https://img.shields.io/github/last-commit/yannickcr/eslint-plugin-react.svg
+[status-image]: https://img.shields.io/badge/status-maintained-brightgreen.svg
diff --git a/appveyor.yml b/appveyor.yml
index 05ed0fda..0812e4f1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,46 +1,23 @@
-version: '{build}'
-skip_branch_with_pr: true
-build: off
-shallow_clone: true
-clone_depth: 1
-
 environment:
   matrix:
-    - nodejs_version: "10"
-    - nodejs_version: "9"
-    - nodejs_version: "8"
-    - nodejs_version: "7"
-    - nodejs_version: "6"
-    - nodejs_version: "5"
-    - nodejs_version: "4"
+    - nodejs_version: '4'
+    - nodejs_version: '5'
+    - nodejs_version: '6'
+    - nodejs_version: '7'
+    - nodejs_version: '8'
+install:
+  - ps: Install-Product node $env:nodejs_version
+  - set CI=true
+  - npm -g install npm@latest
+  - set PATH=%APPDATA%\npm;%PATH%
+  - npm install
 matrix:
   fast_finish: true
-  allow_failures:
-    - nodejs_version: "9"
-    - nodejs_version: "7"
-    - nodejs_version: "5"
-
-platform:
-  - x86
-  - x64
-
-# Install scripts. (runs after repo cloning)
-install:
- # Get the latest stable version of Node.js or io.js
- - ps: Install-Product node $env:nodejs_version $env:platform
- - set CI=true
- - IF %nodejs_version% EQU 0.6 npm -g install npm@1.3
- - IF %nodejs_version% EQU 0.8 npm -g install npm@2
- - IF %nodejs_version% LSS 6 npm -g install npm@5
- - IF %nodejs_version% GEQ 6 npm -g install npm@6
- - set PATH=%APPDATA%\npm;%PATH%
- # install modules
- - npm install
-
-# Post-install test scripts.
+build: off
+version: '{build}'
+shallow_clone: true
+clone_depth: 1
 test_script:
- # Output useful info for debugging.
- - node --version
- - npm --version
- # run tests
- - npm run unit-test
+  - node --version
+  - npm --version
+  - npm test
diff --git a/docs/rules/jsx-key.md b/docs/rules/jsx-key.md
index 6e7b71ef..75b5b5c9 100644
--- a/docs/rules/jsx-key.md
+++ b/docs/rules/jsx-key.md
@@ -11,20 +11,14 @@ The following patterns are considered warnings:
 [<Hello />, <Hello />, <Hello />];
 
 data.map(x => <Hello>{x}</Hello>);
-
-<Hello {...{ key: id, id, caption }} />
 ```
 
-In the last example the key is being spread, which is currently possible, but discouraged in favor of the statically provided key.
-
 The following patterns are **not** considered warnings:
 
 ```jsx
 [<Hello key="first" />, <Hello key="second" />, <Hello key="third" />];
 
 data.map((x, i) => <Hello key={i}>{x}</Hello>);
-
-<Hello key={id} {...{ id, caption }} />
 ```
 
 ## When not to use
diff --git a/docs/rules/jsx-no-literals.md b/docs/rules/jsx-no-literals.md
index 3933357f..5478aaa0 100644
--- a/docs/rules/jsx-no-literals.md
+++ b/docs/rules/jsx-no-literals.md
@@ -27,8 +27,8 @@ There is only one option:
 
 To use, you can specify like the following:
 
-```js
-"react/jsx-no-literals": [<enabled>, {"noStrings": true}]
+```json
+"react/jsx-no-literals": [{"noStrings": true}]
 ```
 
 In this configuration, the following are considered warnings:
diff --git a/docs/rules/jsx-props-no-multi-spaces.md b/docs/rules/jsx-props-no-multi-spaces.md
new file mode 100644
index 00000000..c2ef97e5
--- /dev/null
+++ b/docs/rules/jsx-props-no-multi-spaces.md
@@ -0,0 +1,29 @@
+# Disallow multiple spaces between inline JSX props
+
+Enforces that there is exactly one space between two JSX attributes or the JSX tag name and the first JSX attribute in the same line.
+
+## Rule Details
+
+The following patterns are considered warnings:
+
+```jsx
+<App  spacy />
+```
+
+```jsx
+<App too  spacy />
+```
+
+The following patterns are **not** considered warnings:
+
+```jsx
+<App cozy />
+```
+
+```jsx
+<App very cozy />
+```
+
+## When Not To Use It
+
+If you are not using JSX or don't care about the space between two props in the same line.
diff --git a/docs/rules/no-deprecated.md b/docs/rules/no-deprecated.md
index 63076fed..77cf0ff7 100644
--- a/docs/rules/no-deprecated.md
+++ b/docs/rules/no-deprecated.md
@@ -27,27 +27,6 @@ const propTypes = {
 React.DOM.div();
 
 import React, { PropTypes } from 'react';
-
-class Foo extends React.Component {
-  componentWillMount() { }
-  componentWillReceiveProps() { }
-  componentWillUpdate() { }
-  // ...
-}
-
-class Foo extends React.PureComponent {
-  componentWillMount() { }
-  componentWillReceiveProps() { }
-  componentWillUpdate() { }
-  // ...
-}
-
-var Foo = createReactClass({
-  componentWillMount: function() {},
-  componentWillReceiveProps: function() {},
-  componentWillUpdate: function() {},
-  // ...
-})
 ```
 
 The following patterns are **not** considered warnings:
@@ -59,10 +38,4 @@ ReactDOM.render(<MyComponent />, root);
 ReactDOM.findDOMNode(this.refs.foo);
 
 import { PropTypes } from 'prop-types';
-
-class Foo {
-  componentWillMount() { }
-  componentWillReceiveProps() { }
-  componentWillUpdate() { }
-}
 ```
diff --git a/docs/rules/no-typos.md b/docs/rules/no-typos.md
index 87d0a94f..50a86c19 100644
--- a/docs/rules/no-typos.md
+++ b/docs/rules/no-typos.md
@@ -17,18 +17,12 @@ no casing typos:
 
 and the following react lifecycle methods:
 
-* getDerivedStateFromProps
 * componentWillMount
-* UNSAFE_componentWillMount
 * componentDidMount
 * componentWillReceiveProps
-* UNSAFE_componentWillReceiveProps
 * shouldComponentUpdate
 * componentWillUpdate
-* UNSAFE_componentWillUpdate
-* getSnapshotBeforeUpdate
 * componentDidUpdate
-* componentDidCatch
 * componentWillUnmount
 * render
 
diff --git a/docs/rules/sort-comp.md b/docs/rules/sort-comp.md
index 1e03d12e..709ed55e 100644
--- a/docs/rules/sort-comp.md
+++ b/docs/rules/sort-comp.md
@@ -1,6 +1,6 @@
 # Enforce component methods order (react/sort-comp)
 
-When creating React components it is more convenient to always follow the same organisation for method order to help you easily find lifecycle methods, event handlers, etc.
+When creating React components it is more convenient to always follow the same organisation for method order to help you easily find lifecyle methods, event handlers, etc.
 
 **Fixable:** This rule is automatically fixable using the [`sort-comp` transform](https://github.com/reactjs/react-codemod/blob/master/transforms/sort-comp.js) in [react-codemod](https://www.npmjs.com/package/react-codemod).
 
@@ -9,7 +9,7 @@ When creating React components it is more convenient to always follow the same o
 The default configuration ensures that the following order must be followed:
 
   1. static methods and properties
-  2. lifecycle methods: `displayName`, `propTypes`, `contextTypes`, `childContextTypes`, `mixins`, `statics`, `defaultProps`, `constructor`, `getDefaultProps`, `state`, `getInitialState`, `getChildContext`, `getDerivedStateFromProps`, `componentWillMount`, `UNSAFE_componentWillMount`, `componentDidMount`, `componentWillReceiveProps`, `UNSAFE_componentWillReceiveProps`, `shouldComponentUpdate`, `componentWillUpdate`, `UNSAFE_componentWillUpdate`, `getSnapshotBeforeUpdate`, `componentDidUpdate`, `componentDidCatch`, `componentWillUnmount` (in this order).
+  2. lifecycle methods: `displayName`, `propTypes`, `contextTypes`, `childContextTypes`, `mixins`, `statics`,`defaultProps`, `constructor`, `getDefaultProps`, `getInitialState`, `state`, `getChildContext`, `componentWillMount`, `componentDidMount`, `componentWillReceiveProps`, `shouldComponentUpdate`, `componentWillUpdate`, `componentDidUpdate`, `componentWillUnmount` (in this order).
   3. custom methods
   4. `render` method
 
@@ -70,21 +70,15 @@ The default configuration is:
       'defaultProps',
       'constructor',
       'getDefaultProps',
-      'state',
       'getInitialState',
+      'state',
       'getChildContext',
-      'getDerivedStateFromProps',
       'componentWillMount',
-      'UNSAFE_componentWillMount',
       'componentDidMount',
       'componentWillReceiveProps',
-      'UNSAFE_componentWillReceiveProps',
       'shouldComponentUpdate',
       'componentWillUpdate',
-      'UNSAFE_componentWillUpdate',
-      'getSnapshotBeforeUpdate',
       'componentDidUpdate',
-      'componentDidCatch',
       'componentWillUnmount'
     ]
   }
@@ -92,14 +86,14 @@ The default configuration is:
 ```
 
 * `static-methods` is a special keyword that refers to static class methods.
-* `lifecycle` refers to the `lifecycle` group defined in `groups`.
-* `everything-else` is a special group that matches all of the methods that do not match any of the other groups.
-* `render` refers to the `render` method.
-* `type-annotations`. This group is not specified by default, but can be used to enforce flow annotations' positioning.
-* `getters` This group is not specified by default, but can be used to enforce class getters' positioning.
-* `setters` This group is not specified by default, but can be used to enforce class setters' positioning.
-* `instance-variables` This group is not specified by default, but can be used to enforce all other instance variables' positioning.
-* `instance-methods` This group is not specified by default, but can be used to enforce all other instance methods' positioning.
+* `lifecycle` is referring to the `lifecycle` group defined in `groups`.
+* `everything-else` is a special group that match all the methods that do not match any of the other groups.
+* `render` is referring to the `render` method.
+* `type-annotations`. This group is not specified by default, but can be used to enforce flow annotations positioning.
+* `getters` This group is not specified by default, but can be used to enforce class getters positioning.
+* `setters` This group is not specified by default, but can be used to enforce class setters positioning.
+* `instance-variables` This group is not specified by default, but can be used to enforce all other instance variables positioning.
+* `instance-methods` This group is not specified by default, but can be used to enforce all other instance methods positioning.
 
 You can override this configuration to match your needs.
 
@@ -223,6 +217,7 @@ class Hello extends React.Component<any, Props, void> {
 }
 ```
 
+
 ## When Not To Use It
 
 This rule is a formatting preference and not following it won't negatively affect the quality of your code. If components organisation isn't a part of your coding standards, then you can leave this rule off.
diff --git a/index.js b/index.js
index e0b12d3f..e311f354 100644
--- a/index.js
+++ b/index.js
@@ -36,6 +36,7 @@ const allRules = {
   'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
   'jsx-curly-brace-presence': require('./lib/rules/jsx-curly-brace-presence'),
   'jsx-pascal-case': require('./lib/rules/jsx-pascal-case'),
+  'jsx-props-no-multi-spaces': require('./lib/rules/jsx-props-no-multi-spaces'),
   'jsx-sort-default-props': require('./lib/rules/jsx-sort-default-props'),
   'jsx-sort-props': require('./lib/rules/jsx-sort-props'),
   'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js
index 3d414a72..ccfdf6f3 100644
--- a/lib/rules/boolean-prop-naming.js
+++ b/lib/rules/boolean-prop-naming.js
@@ -76,10 +76,7 @@ module.exports = {
       if (node.value.property) {
         const name = node.value.property.name;
         if (name === 'isRequired') {
-          if (node.value.object && node.value.object.property) {
-            return node.value.object.property.name;
-          }
-          return null;
+          return node.value.object.property.name;
         }
         return name;
       }
diff --git a/lib/rules/jsx-props-no-multi-spaces.js b/lib/rules/jsx-props-no-multi-spaces.js
new file mode 100644
index 00000000..e9a55893
--- /dev/null
+++ b/lib/rules/jsx-props-no-multi-spaces.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview Disallow multiple spaces between inline JSX props
+ * @author Adrian Moennich
+ */
+
+'use strict';
+
+const docsUrl = require('../util/docsUrl');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    docs: {
+      description: 'Disallow multiple spaces between inline JSX props',
+      category: 'Stylistic Issues',
+      recommended: false,
+      url: docsUrl('jsx-props-no-multi-spaces')
+    },
+    fixable: 'code',
+    schema: []
+  },
+
+  create: function (context) {
+    const sourceCode = context.getSourceCode();
+
+    function getPropName(propNode) {
+      if (propNode.type === 'JSXSpreadAttribute') {
+        return sourceCode.getText(propNode.argument);
+      } else if (propNode.type === 'JSXIdentifier') {
+        return propNode.name;
+      }
+      return propNode.name.name;
+    }
+
+    function checkSpacing(prev, node) {
+      if (prev.loc.end.line !== node.loc.end.line) {
+        return;
+      }
+      const between = sourceCode.text.slice(prev.range[1], node.range[0]);
+      if (between !== ' ') {
+        context.report({
+          node: node,
+          message: `Expected only one space between "${getPropName(prev)}" and "${getPropName(node)}"`,
+          fix: function(fixer) {
+            return fixer.replaceTextRange([prev.range[1], node.range[0]], ' ');
+          }
+        });
+      }
+    }
+
+    return {
+      JSXOpeningElement: function (node) {
+        node.attributes.reduce((prev, prop) => {
+          checkSpacing(prev, prop);
+          return prop;
+        }, node.name);
+      }
+    };
+  }
+};
diff --git a/lib/rules/no-access-state-in-setstate.js b/lib/rules/no-access-state-in-setstate.js
index e656d57f..9d5401c1 100644
--- a/lib/rules/no-access-state-in-setstate.js
+++ b/lib/rules/no-access-state-in-setstate.js
@@ -47,7 +47,7 @@ module.exports = {
     return {
       CallExpression(node) {
         // Appends all the methods that are calling another
-        // method containing this.state to the methods array
+        // method containg this.state to the methods array
         methods.map(method => {
           if (node.callee.name === method.methodName) {
             let current = node.parent;
diff --git a/lib/rules/no-deprecated.js b/lib/rules/no-deprecated.js
index c5dee54f..6e8c88e2 100644
--- a/lib/rules/no-deprecated.js
+++ b/lib/rules/no-deprecated.js
@@ -2,17 +2,14 @@
  * @fileoverview Prevent usage of deprecated methods
  * @author Yannick Croissant
  * @author Scott Feeney
- * @author Sergei Startsev
  */
 'use strict';
 
 const has = require('has');
 
-const Components = require('../util/Components');
-const astUtil = require('../util/ast');
-const docsUrl = require('../util/docsUrl');
 const pragmaUtil = require('../util/pragma');
 const versionUtil = require('../util/version');
+const docsUrl = require('../util/docsUrl');
 
 // ------------------------------------------------------------------------------
 // Constants
@@ -23,7 +20,7 @@ const MODULES = {
   'react-addons-perf': ['ReactPerf', 'Perf']
 };
 
-const DEPRECATED_MESSAGE = '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}{{refs}}';
+const DEPRECATED_MESSAGE = '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}';
 
 // ------------------------------------------------------------------------------
 // Rule Definition
@@ -40,7 +37,7 @@ module.exports = {
     schema: []
   },
 
-  create: Components.detect((context, components, utils) => {
+  create: function(context) {
     const sourceCode = context.getSourceCode();
     const pragma = pragmaUtil.getFromContext(context);
 
@@ -76,22 +73,6 @@ module.exports = {
       deprecated[`${pragma}.PropTypes`] = ['15.5.0', 'the npm module prop-types'];
       // 15.6.0
       deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories'];
-      // 16.3.0
-      deprecated.componentWillMount = [
-        '16.3.0',
-        'UNSAFE_componentWillMount',
-        'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-      ];
-      deprecated.componentWillReceiveProps = [
-        '16.3.0',
-        'UNSAFE_componentWillReceiveProps',
-        'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-      ];
-      deprecated.componentWillUpdate = [
-        '16.3.0',
-        'UNSAFE_componentWillUpdate',
-        'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-      ];
       return deprecated;
     }
 
@@ -101,7 +82,6 @@ module.exports = {
       return (
         deprecated &&
         deprecated[method] &&
-        deprecated[method][0] &&
         versionUtil.testReactVersion(context, deprecated[method][0])
       );
     }
@@ -111,17 +91,13 @@ module.exports = {
         return;
       }
       const deprecated = getDeprecated();
-      const version = deprecated[method][0];
-      const newMethod = deprecated[method][1];
-      const refs = deprecated[method][2];
       context.report({
         node: node,
         message: DEPRECATED_MESSAGE,
         data: {
           oldMethod: method,
-          version,
-          newMethod: newMethod ? `, use ${newMethod} instead` : '',
-          refs: refs ? `, see ${refs}` : ''
+          version: deprecated[method][0],
+          newMethod: deprecated[method][1] ? `, use ${deprecated[method][1]} instead` : ''
         }
       });
     }
@@ -143,27 +119,6 @@ module.exports = {
       return moduleName;
     }
 
-    /**
-     * Returns life cycle methods if available
-     * @param {ASTNode} node The AST node being checked.
-     * @returns {Array} The array of methods.
-     */
-    function getLifeCycleMethods(node) {
-      const properties = astUtil.getComponentProperties(node);
-      return properties.map(property => astUtil.getPropertyName(property));
-    }
-
-    /**
-     * Checks life cycle methods
-     * @param {ASTNode} node The AST node being checked.
-     */
-    function checkLifeCycleMethods(node) {
-      if (utils.isES5Component(node) || utils.isES6Component(node)) {
-        const methods = getLifeCycleMethods(node);
-        methods.forEach(method => checkDeprecation(node, method));
-      }
-    }
-
     // --------------------------------------------------------------------------
     // Public
     // --------------------------------------------------------------------------
@@ -190,10 +145,10 @@ module.exports = {
       VariableDeclarator: function(node) {
         const reactModuleName = getReactModuleName(node);
         const isRequire = node.init && node.init.callee && node.init.callee.name === 'require';
-        const isReactRequire = node.init
-          && node.init.arguments
-          && node.init.arguments.length
-          && typeof MODULES[node.init.arguments[0].value] !== 'undefined';
+        const isReactRequire =
+          node.init && node.init.arguments &&
+          node.init.arguments.length && typeof MODULES[node.init.arguments[0].value] !== 'undefined'
+        ;
         const isDestructuring = node.id && node.id.type === 'ObjectPattern';
 
         if (
@@ -205,11 +160,8 @@ module.exports = {
         node.id.properties.forEach(property => {
           checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`);
         });
-      },
+      }
 
-      ClassDeclaration: checkLifeCycleMethods,
-      ClassExpression: checkLifeCycleMethods,
-      ObjectExpression: checkLifeCycleMethods
     };
-  })
+  }
 };
diff --git a/lib/rules/no-typos.js b/lib/rules/no-typos.js
index b85db837..1e350113 100644
--- a/lib/rules/no-typos.js
+++ b/lib/rules/no-typos.js
@@ -12,18 +12,12 @@ const docsUrl = require('../util/docsUrl');
 
 const STATIC_CLASS_PROPERTIES = ['propTypes', 'contextTypes', 'childContextTypes', 'defaultProps'];
 const LIFECYCLE_METHODS = [
-  'getDerivedStateFromProps',
   'componentWillMount',
-  'UNSAFE_componentWillMount',
   'componentDidMount',
   'componentWillReceiveProps',
-  'UNSAFE_componentWillReceiveProps',
   'shouldComponentUpdate',
   'componentWillUpdate',
-  'UNSAFE_componentWillUpdate',
-  'getSnapshotBeforeUpdate',
   'componentDidUpdate',
-  'componentDidCatch',
   'componentWillUnmount',
   'render'
 ];
diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js
index b73c173c..6f530119 100644
--- a/lib/rules/no-unused-prop-types.js
+++ b/lib/rules/no-unused-prop-types.js
@@ -23,7 +23,7 @@ const DIRECT_PROPS_REGEX = /^props\s*(\.|\[)/;
 const DIRECT_NEXT_PROPS_REGEX = /^nextProps\s*(\.|\[)/;
 const DIRECT_PREV_PROPS_REGEX = /^prevProps\s*(\.|\[)/;
 const LIFE_CYCLE_METHODS = ['componentWillReceiveProps', 'shouldComponentUpdate', 'componentWillUpdate', 'componentDidUpdate'];
-const ASYNC_SAFE_LIFE_CYCLE_METHODS = ['getDerivedStateFromProps', 'getSnapshotBeforeUpdate', 'UNSAFE_componentWillReceiveProps', 'UNSAFE_componentWillUpdate'];
+const ASYNC_SAFE_LIFE_CYCLE_METHODS = ['getDerivedStateFromProps', 'UNSAFE_componentWillReceiveProps', 'UNSAFE_componentWillUpdate'];
 
 // ------------------------------------------------------------------------------
 // Rule Definition
diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js
index 88caa962..f140491f 100644
--- a/lib/rules/no-unused-state.js
+++ b/lib/rules/no-unused-state.js
@@ -77,7 +77,7 @@ module.exports = {
     // JSX attributes), then this is again set to null.
     let classInfo = null;
 
-    // Returns true if the given node is possibly a reference to `this.state`, `prevState` or `nextState`.
+    // Returns true if the given node is possibly a reference to `this.state`.
     function isStateReference(node) {
       node = uncast(node);
 
@@ -91,15 +91,7 @@ module.exports = {
         classInfo.aliases &&
         classInfo.aliases.has(node.name);
 
-      const isPrevStateReference =
-        node.type === 'Identifier' &&
-        node.name === 'prevState';
-
-      const isNextStateReference =
-        node.type === 'Identifier' &&
-        node.name === 'nextState';
-
-      return isDirectStateReference || isAliasedStateReference || isPrevStateReference || isNextStateReference;
+      return isDirectStateReference || isAliasedStateReference;
     }
 
     // Takes an ObjectExpression node and adds all named Property nodes to the
diff --git a/lib/rules/react-in-jsx-scope.js b/lib/rules/react-in-jsx-scope.js
index c9a39f92..492f9e3c 100644
--- a/lib/rules/react-in-jsx-scope.js
+++ b/lib/rules/react-in-jsx-scope.js
@@ -27,23 +27,22 @@ module.exports = {
     const pragma = pragmaUtil.getFromContext(context);
     const NOT_DEFINED_MESSAGE = '\'{{name}}\' must be in scope when using JSX';
 
-    function checkIfReactIsInScope(node) {
-      const variables = variableUtil.variablesInScope(context);
-      if (variableUtil.findVariable(variables, pragma)) {
-        return;
-      }
-      context.report({
-        node: node,
-        message: NOT_DEFINED_MESSAGE,
-        data: {
-          name: pragma
+    return {
+
+      JSXOpeningElement: function(node) {
+        const variables = variableUtil.variablesInScope(context);
+        if (variableUtil.findVariable(variables, pragma)) {
+          return;
         }
-      });
-    }
+        context.report({
+          node: node,
+          message: NOT_DEFINED_MESSAGE,
+          data: {
+            name: pragma
+          }
+        });
+      }
 
-    return {
-      JSXOpeningElement: checkIfReactIsInScope,
-      JSXOpeningFragment: checkIfReactIsInScope
     };
   }
 };
diff --git a/lib/rules/sort-comp.js b/lib/rules/sort-comp.js
index def7f9a6..e5e141fc 100644
--- a/lib/rules/sort-comp.js
+++ b/lib/rules/sort-comp.js
@@ -32,18 +32,12 @@ const defaultConfig = {
       'state',
       'getInitialState',
       'getChildContext',
-      'getDerivedStateFromProps',
       'componentWillMount',
-      'UNSAFE_componentWillMount',
       'componentDidMount',
       'componentWillReceiveProps',
-      'UNSAFE_componentWillReceiveProps',
       'shouldComponentUpdate',
       'componentWillUpdate',
-      'UNSAFE_componentWillUpdate',
-      'getSnapshotBeforeUpdate',
       'componentDidUpdate',
-      'componentDidCatch',
       'componentWillUnmount'
     ]
   }
@@ -137,6 +131,13 @@ module.exports = {
       let j;
       const indexes = [];
 
+      if (method.static) {
+        const staticIndex = methodsOrder.indexOf('static-methods');
+        if (staticIndex >= 0) {
+          indexes.push(staticIndex);
+        }
+      }
+
       if (method.getter) {
         const getterIndex = methodsOrder.indexOf('getters');
         if (getterIndex >= 0) {
@@ -158,6 +159,8 @@ module.exports = {
         }
       }
 
+      // Either this is not a static method or static methods are not specified
+      // in the methodsOrder.
       if (indexes.length === 0) {
         for (i = 0, j = methodsOrder.length; i < j; i++) {
           isRegExp = methodsOrder[i].match(regExpRegExp);
@@ -172,13 +175,6 @@ module.exports = {
         }
       }
 
-      if (method.static) {
-        const staticIndex = methodsOrder.indexOf('static-methods');
-        if (staticIndex >= 0) {
-          indexes.push(staticIndex);
-        }
-      }
-
       if (indexes.length === 0 && method.instanceVariable) {
         const annotationIndex = methodsOrder.indexOf('instance-variables');
         if (annotationIndex >= 0) {
diff --git a/lib/util/version.js b/lib/util/version.js
index 252d4f59..cbdec7d9 100644
--- a/lib/util/version.js
+++ b/lib/util/version.js
@@ -27,7 +27,7 @@ function getFlowVersionFromContext(context) {
 }
 
 function test(context, methodVer, confVer) {
-  methodVer = String(methodVer || '').split('.').map(part => Number(part));
+  methodVer = methodVer.split('.').map(part => Number(part));
   const higherMajor = methodVer[0] < confVer[0];
   const higherMinor = methodVer[0] === confVer[0] && methodVer[1] < confVer[1];
   const higherOrEqualPatch = methodVer[0] === confVer[0] && methodVer[1] === confVer[1] && methodVer[2] <= confVer[2];
diff --git a/package.json b/package.json
index 917be882..217dd81b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-react",
-  "version": "7.8.2",
+  "version": "7.7.0",
   "author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",
   "description": "React specific linting rules for ESLint",
   "main": "index.js",


Test patch:
diff --git a/tests/lib/rules/boolean-prop-naming.js b/tests/lib/rules/boolean-prop-naming.js
index e90fc397..f83c8dd9 100644
--- a/tests/lib/rules/boolean-prop-naming.js
+++ b/tests/lib/rules/boolean-prop-naming.js
@@ -348,17 +348,6 @@ ruleTester.run('boolean-prop-naming', rule, {
         hasValue: PropTypes.bool.isRequired
       }
     `
-  }, {
-    // Ensure the rule does not throw when a shape prop isRequired.
-    code: `
-      var Hello = createReactClass({
-        propTypes: {something: PropTypes.shape({}).isRequired},
-        render: function() { return <div />; }
-      });
-    `,
-    options: [{
-      rule: '^is[A-Z]([A-Za-z0-9]?)+'
-    }]
   }],
 
   invalid: [{
diff --git a/tests/lib/rules/jsx-curly-spacing.js b/tests/lib/rules/jsx-curly-spacing.js
index 3fa35be8..18078f95 100644
--- a/tests/lib/rules/jsx-curly-spacing.js
+++ b/tests/lib/rules/jsx-curly-spacing.js
@@ -2256,18 +2256,5 @@ ruleTester.run('jsx-curly-spacing', rule, {
     }, {
       message: 'A space is required before \'}\''
     }]
-  }, {
-    code: [
-      '<div className={ this.state.renderInfo ? "infoPanel col-xs-12" : "unToggled col-xs-12" } />'
-    ].join('\n'),
-    output: [
-      '<div className={this.state.renderInfo ? "infoPanel col-xs-12" : "unToggled col-xs-12"} />'
-    ].join('\n'),
-    options: ['never', {allowMultiline: true}],
-    errors: [{
-      message: 'There should be no space after \'{\''
-    }, {
-      message: 'There should be no space before \'}\''
-    }]
   }]
 });
diff --git a/tests/lib/rules/jsx-props-no-multi-spaces.js b/tests/lib/rules/jsx-props-no-multi-spaces.js
new file mode 100644
index 00000000..10b11483
--- /dev/null
+++ b/tests/lib/rules/jsx-props-no-multi-spaces.js
@@ -0,0 +1,111 @@
+/**
+ * @fileoverview Disallow multiple spaces between inline JSX props
+ * @author Adrian Moennich
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/jsx-props-no-multi-spaces');
+const RuleTester = require('eslint').RuleTester;
+
+const parserOptions = {
+  ecmaVersion: 8,
+  sourceType: 'module',
+  ecmaFeatures: {
+    experimentalObjectRestSpread: true,
+    jsx: true
+  }
+};
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({parserOptions});
+ruleTester.run('jsx-props-no-multi-spaces', rule, {
+  valid: [{
+    code: [
+      '<App />'
+    ].join('\n')
+  }, {
+    code: [
+      '<App foo />'
+    ].join('\n')
+  }, {
+    code: [
+      '<App foo bar />'
+    ].join('\n')
+  }, {
+    code: [
+      '<App foo="with  spaces   " bar />'
+    ].join('\n')
+  }, {
+    code: [
+      '<App',
+      '  foo bar />'
+    ].join('\n')
+  }, {
+    code: [
+      '<App',
+      '  foo',
+      '  bar />'
+    ].join('\n')
+  }, {
+    code: [
+      '<App',
+      '  foo {...test}',
+      '  bar />'
+    ].join('\n')
+  }],
+
+  invalid: [{
+    code: [
+      '<App  foo />'
+    ].join('\n'),
+    output: [
+      '<App foo />'
+    ].join('\n'),
+    errors: [{message: 'Expected only one space between "App" and "foo"'}]
+  }, {
+    code: [
+      '<App foo="with  spaces   "   bar />'
+    ].join('\n'),
+    output: [
+      '<App foo="with  spaces   " bar />'
+    ].join('\n'),
+    errors: [{message: 'Expected only one space between "foo" and "bar"'}]
+  }, {
+    code: [
+      '<App foo  bar />'
+    ].join('\n'),
+    output: [
+      '<App foo bar />'
+    ].join('\n'),
+    errors: [{message: 'Expected only one space between "foo" and "bar"'}]
+  }, {
+    code: [
+      '<App  foo   bar />'
+    ].join('\n'),
+    output: [
+      '<App foo bar />'
+    ].join('\n'),
+    errors: [
+      {message: 'Expected only one space between "App" and "foo"'},
+      {message: 'Expected only one space between "foo" and "bar"'}
+    ]
+  }, {
+    code: [
+      '<App foo  {...test}  bar />'
+    ].join('\n'),
+    output: [
+      '<App foo {...test} bar />'
+    ].join('\n'),
+    errors: [
+      {message: 'Expected only one space between "foo" and "test"'},
+      {message: 'Expected only one space between "test" and "bar"'}
+    ]
+  }]
+});
diff --git a/tests/lib/rules/no-deprecated.js b/tests/lib/rules/no-deprecated.js
index a1165563..9ddcd6b9 100644
--- a/tests/lib/rules/no-deprecated.js
+++ b/tests/lib/rules/no-deprecated.js
@@ -2,7 +2,6 @@
  * @fileoverview Prevent usage of deprecated methods
  * @author Yannick Croissant
  * @author Scott Feeney
- * @author Sergei Startsev
  */
 'use strict';
 
@@ -24,12 +23,6 @@ const parserOptions = {
 
 require('babel-eslint');
 
-function errorMessage(oldMethod, version, newMethod, refs) {
-  newMethod = newMethod ? `, use ${newMethod} instead` : '';
-  refs = refs ? `, see ${refs}` : '';
-  return `${oldMethod} is deprecated since React ${version}${newMethod}${refs}`;
-}
-
 // ------------------------------------------------------------------------------
 // Tests
 // ------------------------------------------------------------------------------
@@ -46,369 +39,151 @@ ruleTester.run('no-deprecated', rule, {
     'ReactDOM.findDOMNode(instance);',
     'ReactDOMServer.renderToString(element);',
     'ReactDOMServer.renderToStaticMarkup(element);',
-    {
-      code: `
-        var Foo = createReactClass({
-          render: function() {}
-        })
-      `
-    },
-    // Non-React
-    {
-      code: `
-        var Foo = createReactClassNonReact({
-          componentWillMount: function() {},
-          componentWillReceiveProps: function() {},
-          componentWillUpdate: function() {}
-        });
-      `
-    },
-    {
-      code: `
-        var Foo = {
-          componentWillMount: function() {},
-          componentWillReceiveProps: function() {},
-          componentWillUpdate: function() {}
-        };
-      `
-    },
-    {
-      code: `
-        class Foo {
-          constructor() {}
-          componentWillMount() {}
-          componentWillReceiveProps() {}
-          componentWillUpdate() {}
-        }
-      `
-    },
     // Deprecated in a later version
     {code: 'React.renderComponent()', settings: {react: {version: '0.11.0'}}},
     {code: 'React.createClass()', settings: {react: {version: '15.4.0'}}},
-    {code: 'PropTypes', settings: {react: {version: '15.4.0'}}},
-    {
-      code: `
-        class Foo extends React.Component {
-          componentWillMount() {}
-          componentWillReceiveProps() {}
-          componentWillUpdate() {}
-        }
-      `,
-      settings: {react: {version: '16.2.0'}}
-    }
+    {code: 'PropTypes', settings: {react: {version: '15.4.0'}}}
   ],
 
-  invalid: [
-    {
-      code: 'React.renderComponent()',
-      errors: [{message: errorMessage('React.renderComponent', '0.12.0', 'React.render')}]
-    },
-    {
-      code: 'Foo.renderComponent()',
-      settings: {react: {pragma: 'Foo'}},
-      errors: [{message: errorMessage('Foo.renderComponent', '0.12.0', 'Foo.render')}]
-    },
-    {
-      code: '/** @jsx Foo */ Foo.renderComponent()',
-      errors: [{message: errorMessage('Foo.renderComponent', '0.12.0', 'Foo.render')}]
-    },
-    {
-      code: 'this.transferPropsTo()',
-      errors: [{message: errorMessage('this.transferPropsTo', '0.12.0', 'spread operator ({...})')}]
-    },
-    {
-      code: 'React.addons.TestUtils',
-      errors: [{message: errorMessage('React.addons.TestUtils', '15.5.0', 'ReactDOM.TestUtils')}]
-    },
-    {
-      code: 'React.addons.classSet()',
-      errors: [{message: errorMessage('React.addons.classSet', '0.13.0', 'the npm module classnames')}]
-    },
-    {
-      code: 'React.render(element, container);',
-      errors: [{message: errorMessage('React.render', '0.14.0', 'ReactDOM.render')}]
-    },
-    {
-      code: 'React.unmountComponentAtNode(container);',
-      errors: [{message: errorMessage('React.unmountComponentAtNode', '0.14.0', 'ReactDOM.unmountComponentAtNode')}]
-    },
-    {
-      code: 'React.findDOMNode(instance);',
-      errors: [{message: errorMessage('React.findDOMNode', '0.14.0', 'ReactDOM.findDOMNode')}]
-    },
-    {
-      code: 'React.renderToString(element);',
-      errors: [{message: errorMessage('React.renderToString', '0.14.0', 'ReactDOMServer.renderToString')}]
-    },
-    {
-      code: 'React.renderToStaticMarkup(element);',
-      errors: [{message: errorMessage('React.renderToStaticMarkup', '0.14.0', 'ReactDOMServer.renderToStaticMarkup')}]
-    },
-    {
-      code: 'React.createClass({});',
-      errors: [{message: errorMessage('React.createClass', '15.5.0', 'the npm module create-react-class')}]
-    },
-    {
-      code: 'Foo.createClass({});',
-      settings: {react: {pragma: 'Foo'}},
-      errors: [{message: errorMessage('Foo.createClass', '15.5.0', 'the npm module create-react-class')}]
-    },
-    {
-      code: 'React.PropTypes',
-      errors: [{message: errorMessage('React.PropTypes', '15.5.0', 'the npm module prop-types')}]
-    },
-    {
-      code: 'var {createClass} = require(\'react\');',
-      parser: 'babel-eslint',
-      errors: [{message: errorMessage('React.createClass', '15.5.0', 'the npm module create-react-class')}]
-    },
-    {
-      code: 'var {createClass, PropTypes} = require(\'react\');',
-      parser: 'babel-eslint',
-      errors: [
-        {message: errorMessage('React.createClass', '15.5.0', 'the npm module create-react-class')},
-        {message: errorMessage('React.PropTypes', '15.5.0', 'the npm module prop-types')}
-      ]
-    },
-    {
-      code: 'import {createClass} from \'react\';',
-      parser: 'babel-eslint',
-      errors: [{message: errorMessage('React.createClass', '15.5.0', 'the npm module create-react-class')}]
-    },
-    {
-      code: 'import {createClass, PropTypes} from \'react\';',
-      parser: 'babel-eslint',
-      errors: [
-        {message: errorMessage('React.createClass', '15.5.0', 'the npm module create-react-class')},
-        {message: errorMessage('React.PropTypes', '15.5.0', 'the npm module prop-types')}
-      ]
-    },
-    {
-      code: `
+  invalid: [{
+    code: 'React.renderComponent()',
+    settings: {react: {version: '0.12.0'}},
+    errors: [{
+      message: 'React.renderComponent is deprecated since React 0.12.0, use React.render instead'
+    }]
+  }, {
+    code: 'Foo.renderComponent()',
+    settings: {react: {pragma: 'Foo', version: '0.12.0'}},
+    errors: [{
+      message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead'
+    }]
+  }, {
+    code: '/** @jsx Foo */ Foo.renderComponent()',
+    settings: {react: {version: '0.12.0'}},
+    errors: [{
+      message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead'
+    }]
+  }, {
+    code: 'this.transferPropsTo()',
+    errors: [{
+      message: 'this.transferPropsTo is deprecated since React 0.12.0, use spread operator ({...}) instead'
+    }]
+  }, {
+    code: 'React.addons.TestUtils',
+    errors: [{
+      message: 'React.addons.TestUtils is deprecated since React 15.5.0, use ReactDOM.TestUtils instead'
+    }]
+  }, {
+    code: 'React.addons.classSet()',
+    errors: [{
+      message: 'React.addons.classSet is deprecated since React 0.13.0, use the npm module classnames instead'
+    }]
+  }, {
+    code: 'React.render(element, container);',
+    errors: [{
+      message: 'React.render is deprecated since React 0.14.0, use ReactDOM.render instead'
+    }]
+  }, {
+    code: 'React.unmountComponentAtNode(container);',
+    errors: [{
+      message: (
+        'React.unmountComponentAtNode is deprecated since React 0.14.0, ' +
+        'use ReactDOM.unmountComponentAtNode instead'
+      )
+    }]
+  }, {
+    code: 'React.findDOMNode(instance);',
+    errors: [{
+      message: 'React.findDOMNode is deprecated since React 0.14.0, use ReactDOM.findDOMNode instead'
+    }]
+  }, {
+    code: 'React.renderToString(element);',
+    errors: [{
+      message: 'React.renderToString is deprecated since React 0.14.0, use ReactDOMServer.renderToString instead'
+    }]
+  }, {
+    code: 'React.renderToStaticMarkup(element);',
+    errors: [{
+      message: (
+        'React.renderToStaticMarkup is deprecated since React 0.14.0, ' +
+        'use ReactDOMServer.renderToStaticMarkup instead'
+      )
+    }]
+  }, {
+    code: 'React.createClass({});',
+    errors: [{
+      message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead'
+    }]
+  }, {
+    code: 'Foo.createClass({});',
+    settings: {react: {pragma: 'Foo'}},
+    errors: [{
+      message: 'Foo.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead'
+    }]
+  }, {
+    code: 'React.PropTypes',
+    errors: [{
+      message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead'
+    }]
+  }, {
+    code: 'var {createClass} = require(\'react\');',
+    parser: 'babel-eslint',
+    errors: [{
+      message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead'
+    }]
+  }, {
+    code: 'var {createClass, PropTypes} = require(\'react\');',
+    parser: 'babel-eslint',
+    errors: [{
+      message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead'
+    }, {
+      message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead'
+    }]
+  }, {
+    code: 'import {createClass} from \'react\';',
+    parser: 'babel-eslint',
+    errors: [{
+      message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead'
+    }]
+  }, {
+    code: 'import {createClass, PropTypes} from \'react\';',
+    parser: 'babel-eslint',
+    errors: [{
+      message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead'
+    }, {
+      message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead'
+    }]
+  }, {
+    code: `
       import React from 'react';
       const {createClass, PropTypes} = React;
     `,
-      parser: 'babel-eslint',
-      errors: [
-        {message: errorMessage('React.createClass', '15.5.0', 'the npm module create-react-class')},
-        {message: errorMessage('React.PropTypes', '15.5.0', 'the npm module prop-types')}
-      ]
-    },
-    {
-      code: 'import {printDOM} from \'react-addons-perf\';',
-      parser: 'babel-eslint',
-      errors: [{message: errorMessage('ReactPerf.printDOM', '15.0.0', 'ReactPerf.printOperations')}]
-    },
-    {
-      code: `
-        import ReactPerf from 'react-addons-perf';
-        const {printDOM} = ReactPerf;
-      `,
-      parser: 'babel-eslint',
-      errors: [{message: errorMessage('ReactPerf.printDOM', '15.0.0', 'ReactPerf.printOperations')}]
-    },
-    {
-      code: 'React.DOM.div',
-      errors: [{message: errorMessage('React.DOM', '15.6.0', 'the npm module react-dom-factories')}]
-    },
-    {
-      code: `
-        class Bar extends React.PureComponent {
-          componentWillMount() {}
-          componentWillReceiveProps() {}
-          componentWillUpdate() {}
-        };
-      `,
-      errors: [
-        {
-          message: errorMessage(
-            'componentWillMount', '16.3.0', 'UNSAFE_componentWillMount',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-          )
-        },
-        {
-          message: errorMessage(
-            'componentWillReceiveProps', '16.3.0', 'UNSAFE_componentWillReceiveProps',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-          )
-        },
-        {
-          message: errorMessage('componentWillUpdate', '16.3.0', 'UNSAFE_componentWillUpdate',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-          )
-        }
-      ]
-    },
-    {
-      code: `
-        function Foo() {
-          return class Bar extends React.PureComponent {
-            componentWillMount() {}
-            componentWillReceiveProps() {}
-            componentWillUpdate() {}
-          };
-        }
-      `,
-      errors: [
-        {
-          message: errorMessage(
-            'componentWillMount', '16.3.0', 'UNSAFE_componentWillMount',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-          )
-        },
-        {
-          message: errorMessage(
-            'componentWillReceiveProps', '16.3.0', 'UNSAFE_componentWillReceiveProps',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-          )
-        },
-        {
-          message: errorMessage('componentWillUpdate', '16.3.0', 'UNSAFE_componentWillUpdate',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-          )
-        }
-      ]
-    },
-    {
-      code: `
-        class Bar extends PureComponent {
-          componentWillMount() {}
-          componentWillReceiveProps() {}
-          componentWillUpdate() {}
-        };
-      `,
-      errors: [
-        {
-          message: errorMessage(
-            'componentWillMount', '16.3.0', 'UNSAFE_componentWillMount',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-          )
-        },
-        {
-          message: errorMessage(
-            'componentWillReceiveProps', '16.3.0', 'UNSAFE_componentWillReceiveProps',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-          )
-        },
-        {
-          message: errorMessage('componentWillUpdate', '16.3.0', 'UNSAFE_componentWillUpdate',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-          )
-        }
-      ]
-    },
-    {
-      code: `
-        class Foo extends React.Component {
-          componentWillMount() {}
-          componentWillReceiveProps() {}
-          componentWillUpdate() {}
-        }
-      `,
-      errors: [
-        {
-          message: errorMessage(
-            'componentWillMount', '16.3.0', 'UNSAFE_componentWillMount',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-          )
-        },
-        {
-          message: errorMessage(
-            'componentWillReceiveProps', '16.3.0', 'UNSAFE_componentWillReceiveProps',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-          )
-        },
-        {
-          message: errorMessage('componentWillUpdate', '16.3.0', 'UNSAFE_componentWillUpdate',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-          )
-        }
-      ]
-    },
-    {
-      code: `
-        class Foo extends Component {
-          componentWillMount() {}
-          componentWillReceiveProps() {}
-          componentWillUpdate() {}
-        }
-      `,
-      errors: [
-        {
-          message: errorMessage(
-            'componentWillMount', '16.3.0', 'UNSAFE_componentWillMount',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-          )
-        },
-        {
-          message: errorMessage(
-            'componentWillReceiveProps', '16.3.0', 'UNSAFE_componentWillReceiveProps',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-          )
-        },
-        {
-          message: errorMessage('componentWillUpdate', '16.3.0', 'UNSAFE_componentWillUpdate',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-          )
-        }
-      ]
-    },
-    {
-      code: `
-        var Foo = createReactClass({
-          componentWillMount: function() {},
-          componentWillReceiveProps: function() {},
-          componentWillUpdate: function() {}
-        })
-      `,
-      errors: [
-        {
-          message: errorMessage(
-            'componentWillMount', '16.3.0', 'UNSAFE_componentWillMount',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-          )
-        },
-        {
-          message: errorMessage(
-            'componentWillReceiveProps', '16.3.0', 'UNSAFE_componentWillReceiveProps',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-          )
-        },
-        {
-          message: errorMessage('componentWillUpdate', '16.3.0', 'UNSAFE_componentWillUpdate',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-          )
-        }
-      ]
-    },
-    {
-      code: `
-        class Foo extends React.Component {
-          constructor() {}
-          componentWillMount() {}
-          componentWillReceiveProps() {}
-          componentWillUpdate() {}
-        }
-      `,
-      errors: [
-        {
-          message: errorMessage(
-            'componentWillMount', '16.3.0', 'UNSAFE_componentWillMount',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount'
-          )
-        },
-        {
-          message: errorMessage(
-            'componentWillReceiveProps', '16.3.0', 'UNSAFE_componentWillReceiveProps',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops'
-          )
-        },
-        {
-          message: errorMessage('componentWillUpdate', '16.3.0', 'UNSAFE_componentWillUpdate',
-            'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate'
-          )
-        }
-      ]
-    }
-  ]
+    parser: 'babel-eslint',
+    errors: [{
+      message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead'
+    }, {
+      message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead'
+    }]
+  }, {
+    code: 'import {printDOM} from \'react-addons-perf\';',
+    parser: 'babel-eslint',
+    errors: [{
+      message: 'ReactPerf.printDOM is deprecated since React 15.0.0, use ReactPerf.printOperations instead'
+    }]
+  }, {
+    code: `
+      import ReactPerf from 'react-addons-perf';
+      const {printDOM} = ReactPerf;
+    `,
+    parser: 'babel-eslint',
+    errors: [{
+      message: 'ReactPerf.printDOM is deprecated since React 15.0.0, use ReactPerf.printOperations instead'
+    }]
+  },
+  {
+    code: 'React.DOM.div',
+    errors: [{
+      message: 'React.DOM is deprecated since React 15.6.0, use the npm module react-dom-factories instead'
+    }]
+  }]
 });
diff --git a/tests/lib/rules/no-typos.js b/tests/lib/rules/no-typos.js
index a7b9b02a..a6d50c03 100644
--- a/tests/lib/rules/no-typos.js
+++ b/tests/lib/rules/no-typos.js
@@ -732,18 +732,12 @@ ruleTester.run('no-typos', rule, {
   }, {
     code: `
       class Hello extends React.Component {
-        static GetDerivedStateFromProps()  { }
         ComponentWillMount() { }
-        UNSAFE_ComponentWillMount() { }
         ComponentDidMount() { }
         ComponentWillReceiveProps() { }
-        UNSAFE_ComponentWillReceiveProps() { }
         ShouldComponentUpdate() { }
         ComponentWillUpdate() { }
-        UNSAFE_ComponentWillUpdate() { }
-        GetSnapshotBeforeUpdate() { }
         ComponentDidUpdate() { }
-        ComponentDidCatch() { }
         ComponentWillUnmount() { }
         render() {
           return <div>Hello {this.props.name}</div>;
@@ -772,40 +766,16 @@ ruleTester.run('no-typos', rule, {
     }, {
       message: ERROR_MESSAGE_LIFECYCLE_METHOD,
       type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
     }]
   }, {
     code: `
       class Hello extends React.Component {
-        static Getderivedstatefromprops() { }
         Componentwillmount() { }
-        UNSAFE_Componentwillmount() { }
         Componentdidmount() { }
         Componentwillreceiveprops() { }
-        UNSAFE_Componentwillreceiveprops() { }
         Shouldcomponentupdate() { }
         Componentwillupdate() { }
-        UNSAFE_Componentwillupdate() { }
-        Getsnapshotbeforeupdate() { }
         Componentdidupdate() { }
-        Componentdidcatch() { }
         Componentwillunmount() { }
         Render() {
           return <div>Hello {this.props.name}</div>;
@@ -837,40 +807,16 @@ ruleTester.run('no-typos', rule, {
     }, {
       message: ERROR_MESSAGE_LIFECYCLE_METHOD,
       type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
     }]
   }, {
     code: `
       class Hello extends React.Component {
-        static getderivedstatefromprops() { }
         componentwillmount() { }
-        unsafe_componentwillmount() { }
         componentdidmount() { }
         componentwillreceiveprops() { }
-        unsafe_componentwillreceiveprops() { }
         shouldcomponentupdate() { }
         componentwillupdate() { }
-        unsafe_componentwillupdate() { }
-        getsnapshotbeforeupdate() { }
         componentdidupdate() { }
-        componentdidcatch() { }
         componentwillunmount() { }
         render() {
           return <div>Hello {this.props.name}</div>;
@@ -899,24 +845,6 @@ ruleTester.run('no-typos', rule, {
     }, {
       message: ERROR_MESSAGE_LIFECYCLE_METHOD,
       type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
-    }, {
-      message: ERROR_MESSAGE_LIFECYCLE_METHOD,
-      type: 'MethodDefinition'
     }]
   }, {
     code: `
diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js
index b4fb78ae..85556dec 100644
--- a/tests/lib/rules/no-unused-prop-types.js
+++ b/tests/lib/rules/no-unused-prop-types.js
@@ -2880,26 +2880,6 @@ ruleTester.run('no-unused-prop-types', rule, {
       `].join('\n'),
       settings: {react: {version: '16.3.0'}},
       parser: 'babel-eslint'
-    }, {
-      // Simple test of new static getSnapshotBeforeUpdate lifecycle
-      code: [`
-        class MyComponent extends React.Component {
-          static propTypes = {
-            defaultValue: PropTypes.string
-          };
-          getSnapshotBeforeUpdate(prevProps, prevState) {
-            if (prevProps.defaultValue === null) {
-              return 'snapshot';
-            }
-            return null;
-          }
-          render() {
-            return <div />
-          }
-        }
-      `].join('\n'),
-      settings: {react: {version: '16.3.0'}},
-      parser: 'babel-eslint'
     }
   ],
 
@@ -4530,28 +4510,6 @@ ruleTester.run('no-unused-prop-types', rule, {
       errors: [{
         message: '\'defaultValue\' PropType is defined but prop is never used'
       }]
-    }, {
-      code: [`
-        class MyComponent extends React.Component {
-          static propTypes = {
-            defaultValue: PropTypes.string
-          };
-          getSnapshotBeforeUpdate(prevProps, prevState) {
-            if (prevProps.defaultValue === null) {
-              return 'snapshot';
-            }
-            return null;
-          }
-          render() {
-            return <div />
-          }
-        }
-      `].join('\n'),
-      settings: {react: {version: '16.2.0'}},
-      parser: 'babel-eslint',
-      errors: [{
-        message: '\'defaultValue\' PropType is defined but prop is never used'
-      }]
     }
 
     /* , {
diff --git a/tests/lib/rules/no-unused-state.js b/tests/lib/rules/no-unused-state.js
index 9f286f7a..4e627875 100644
--- a/tests/lib/rules/no-unused-state.js
+++ b/tests/lib/rules/no-unused-state.js
@@ -491,49 +491,6 @@ eslintTester.run('no-unused-state', rule, {
         }
       }`,
       parser: 'babel-eslint'
-    },
-    {
-      code: `class ESLintExample extends Component {
-        constructor(props) {
-          super(props);
-          this.state = {
-            id: 123,
-          };
-        }
-        static getDerivedStateFromProps(nextProps, prevState) {
-          if (prevState.id === nextProps.id) {
-            return {
-              selected: true,
-            };
-          }
-          return null;
-        }
-        render() {
-          return (
-            <h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
-          );
-        }
-      }`,
-      parser: 'babel-eslint'
-    },
-    {
-      code: `class ESLintExample extends Component {
-        constructor(props) {
-          super(props);
-          this.state = {
-            id: 123,
-          };
-        }
-        shouldComponentUpdate(nextProps, nextState) {
-          return nextState.id === nextProps.id;
-        }
-        render() {
-          return (
-            <h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
-          );
-        }
-      }`,
-      parser: 'babel-eslint'
     }
   ],
 
diff --git a/tests/lib/rules/react-in-jsx-scope.js b/tests/lib/rules/react-in-jsx-scope.js
index 1bd9201c..3d76709f 100644
--- a/tests/lib/rules/react-in-jsx-scope.js
+++ b/tests/lib/rules/react-in-jsx-scope.js
@@ -36,7 +36,6 @@ ruleTester.run('react-in-jsx-scope', rule, {
   valid: [
     {code: 'var React, App; <App />;'},
     {code: 'var React; <img />;'},
-    {code: 'var React; <>fragment</>;', parser: 'babel-eslint'},
     {code: 'var React; <x-gif />;'},
     {code: 'var React, App, a=1; <App attr={a} />;'},
     {code: 'var React, App, a=1; function elem() { return <App attr={a} />; }'},
@@ -65,10 +64,6 @@ ruleTester.run('react-in-jsx-scope', rule, {
   }, {
     code: 'var a = <img />;',
     errors: [{message: '\'React\' must be in scope when using JSX'}]
-  }, {
-    code: 'var a = <>fragment</>;',
-    parser: 'babel-eslint',
-    errors: [{message: '\'React\' must be in scope when using JSX'}]
   }, {
     code: '/** @jsx React.DOM */ var a = <img />;',
     errors: [{message: '\'React\' must be in scope when using JSX'}]
diff --git a/tests/lib/rules/sort-comp.js b/tests/lib/rules/sort-comp.js
index 739ee55d..ac9da200 100644
--- a/tests/lib/rules/sort-comp.js
+++ b/tests/lib/rules/sort-comp.js
@@ -91,77 +91,6 @@ ruleTester.run('sort-comp', rule, {
         'everything-else'
       ]
     }]
-  }, {
-    // Must validate a full React 16.3 createReactClass class
-    code: [
-      'var Hello = createReactClass({',
-      '  displayName : \'\',',
-      '  propTypes: {},',
-      '  contextTypes: {},',
-      '  childContextTypes: {},',
-      '  mixins: [],',
-      '  statics: {},',
-      '  getDefaultProps: function() {},',
-      '  getInitialState: function() {},',
-      '  getChildContext: function() {},',
-      '  UNSAFE_componentWillMount: function() {},',
-      '  componentDidMount: function() {},',
-      '  UNSAFE_componentWillReceiveProps: function() {},',
-      '  shouldComponentUpdate: function() {},',
-      '  UNSAFE_componentWillUpdate: function() {},',
-      '  getSnapshotBeforeUpdate: function() {},',
-      '  componentDidUpdate: function() {},',
-      '  componentDidCatch: function() {},',
-      '  componentWillUnmount: function() {},',
-      '  render: function() {',
-      '    return <div>Hello</div>;',
-      '  }',
-      '});'
-    ].join('\n')
-  }, {
-    // Must validate React 16.3 lifecycle methods with the default parser
-    code: [
-      'class Hello extends React.Component {',
-      '  constructor() {}',
-      '  static getDerivedStateFromProps() {}',
-      '  UNSAFE_componentWillMount() {}',
-      '  componentDidMount() {}',
-      '  UNSAFE_componentWillReceiveProps() {}',
-      '  shouldComponentUpdate() {}',
-      '  UNSAFE_componentWillUpdate() {}',
-      '  getSnapshotBeforeUpdate() {}',
-      '  componentDidUpdate() {}',
-      '  componentDidCatch() {}',
-      '  componentWillUnmount() {}',
-      '  testInstanceMethod() {}',
-      '  render() { return (<div>Hello</div>); }',
-      '}'
-    ].join('\n')
-  }, {
-    // Must validate a full React 16.3 ES6 class
-    code: [
-      'class Hello extends React.Component {',
-      '  static displayName = \'\'',
-      '  static propTypes = {}',
-      '  static defaultProps = {}',
-      '  constructor() {}',
-      '  state = {}',
-      '  static getDerivedStateFromProps = () => {}',
-      '  UNSAFE_componentWillMount = () => {}',
-      '  componentDidMount = () => {}',
-      '  UNSAFE_componentWillReceiveProps = () => {}',
-      '  shouldComponentUpdate = () => {}',
-      '  UNSAFE_componentWillUpdate = () => {}',
-      '  getSnapshotBeforeUpdate = () => {}',
-      '  componentDidUpdate = () => {}',
-      '  componentDidCatch = () => {}',
-      '  componentWillUnmount = () => {}',
-      '  testArrowMethod = () => {}',
-      '  testInstanceMethod() {}',
-      '  render = () => (<div>Hello</div>)',
-      '}'
-    ].join('\n'),
-    parser: 'babel-eslint'
   }, {
     // Must allow us to create a RegExp-based group
     code: [