Explorar el Código

Cleanup React components constructor

In most situations the constructor does not need to be overridden for
React components. Function binding can be done using an ES6 decorator
and state initialisation can be done outside the constructor.

Also includes an updated React linting package and rules so that the
order / sorting of the React components' functions and variables are
improved.
Sergiu Miclea hace 7 años
padre
commit
87cc80c137
Se han modificado 45 ficheros con 319 adiciones y 425 borrados
  1. 50 16
      .eslintrc
  2. 2 1
      package.json
  3. 2 6
      src/components/atoms/PasswordValue/PasswordValue.jsx
  4. 2 6
      src/components/atoms/Switch/Switch.jsx
  5. 10 17
      src/components/molecules/AutocompleteDropdown/AutocompleteDropdown.jsx
  6. 8 15
      src/components/molecules/DatetimePicker/DatetimePicker.jsx
  7. 8 15
      src/components/molecules/Dropdown/Dropdown.jsx
  8. 6 11
      src/components/molecules/DropdownFilter/DropdownFilter.jsx
  9. 7 12
      src/components/molecules/DropdownLink/DropdownLink.jsx
  10. 2 7
      src/components/molecules/Modal/Modal.jsx
  11. 6 11
      src/components/molecules/NewItemDropdown/NewItemDropdown.jsx
  12. 4 9
      src/components/molecules/NotificationDropdown/NotificationDropdown.jsx
  13. 12 16
      src/components/molecules/SearchInput/SearchInput.jsx
  14. 2 6
      src/components/molecules/SideMenu/SideMenu.jsx
  15. 6 11
      src/components/molecules/UserDropdown/UserDropdown.jsx
  16. 5 9
      src/components/organisms/AssessmentMigrationOptions/AssessmentMigrationOptions.jsx
  17. 9 13
      src/components/organisms/Endpoint/Endpoint.jsx
  18. 2 6
      src/components/organisms/Executions/Executions.jsx
  19. 7 10
      src/components/organisms/FilterList/FilterList.jsx
  20. 3 7
      src/components/organisms/LoginForm/LoginForm.jsx
  21. 1 6
      src/components/organisms/Notifications/Notifications.jsx
  22. 9 12
      src/components/organisms/PageHeader/PageHeader.jsx
  23. 2 2
      src/components/organisms/ProjectDetailsContent/ProjectDetailsContent.jsx
  24. 2 6
      src/components/organisms/ReplicaDetailsContent/ReplicaDetailsContent.jsx
  25. 2 6
      src/components/organisms/ReplicaExecutionOptions/ReplicaExecutionOptions.jsx
  26. 18 21
      src/components/organisms/ReplicaMigrationOptions/ReplicaMigrationOptions.jsx
  27. 5 9
      src/components/organisms/Schedule/Schedule.jsx
  28. 4 8
      src/components/organisms/Tasks/Tasks.jsx
  29. 5 9
      src/components/organisms/WizardInstances/WizardInstances.jsx
  30. 2 7
      src/components/organisms/WizardOptions/WizardOptions.jsx
  31. 3 7
      src/components/organisms/WizardPageContent/WizardPageContent.jsx
  32. 8 12
      src/components/pages/AssessmentDetailsPage/AssessmentDetailsPage.jsx
  33. 3 3
      src/components/pages/AssessmentsPage/AssessmentsPage.jsx
  34. 7 11
      src/components/pages/EndpointDetailsPage/EndpointDetailsPage.jsx
  35. 12 16
      src/components/pages/EndpointsPage/EndpointsPage.jsx
  36. 5 9
      src/components/pages/MigrationDetailsPage/MigrationDetailsPage.jsx
  37. 7 11
      src/components/pages/MigrationsPage/MigrationsPage.jsx
  38. 3 3
      src/components/pages/ProjectsPage/ProjectsPage.jsx
  39. 10 14
      src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.jsx
  40. 6 10
      src/components/pages/ReplicasPage/ReplicasPage.jsx
  41. 3 3
      src/components/pages/UsersPage/UsersPage.jsx
  42. 8 11
      src/components/pages/WizardPage/WizardPage.jsx
  43. 5 9
      src/plugins/endpoint/azure/ContentPlugin.jsx
  44. 5 8
      src/plugins/endpoint/openstack/ContentPlugin.jsx
  45. 31 8
      yarn.lock

+ 50 - 16
.eslintrc

@@ -22,22 +22,56 @@
     }
   },
   "rules": {
-    "react/sort-comp": [1, {
-      "order": [
-        "static-methods",
-        "type-annotations",
-        "lifecycle",
-        "/^set.+$/",
-        "/^get.+$/",
-        "/^is.+$/",
-        "/^has.+$/",
-        "/^handle.+$/",
-        "everything-else",
-        "render"
-      ]
-    }],
-    "semi": [2, "never"],
-    "comma-dangle": [2, "always-multiline"],
+    "react/sort-comp": [
+      1,
+      {
+        "order": [
+          "static-methods",
+          "displayName",
+          "propTypes",
+          "contextTypes",
+          "childContextTypes",
+          "mixins",
+          "statics",
+          "defaultProps",
+          "state",
+          "type-annotations",
+          "instance-variables",
+          "getters",
+          "setters",
+          "constructor",
+          "getDefaultProps",
+          "getInitialState",
+          "getChildContext",
+          "getDerivedStateFromProps",
+          "lifecycle",
+          "everything-else",
+          "^handle.+$",
+          "render"
+        ],
+        "groups": {
+          "lifecycle": [
+            "componentWillMount",
+            "componentDidMount",
+            "componentWillReceiveProps",
+            "shouldComponentUpdate",
+            "componentWillUpdate",
+            "getSnapshotBeforeUpdate",
+            "componentDidUpdate",
+            "componentDidCatch",
+            "componentWillUnmount"
+          ]
+        }
+      }
+    ],
+    "semi": [
+      2,
+      "never"
+    ],
+    "comma-dangle": [
+      2,
+      "always-multiline"
+    ],
     "newline-per-chained-call": 0,
     "class-methods-use-this": 0,
     "max-len": 0,

+ 2 - 1
package.json

@@ -46,7 +46,7 @@
     "eslint-plugin-flowtype": "^2.46.1",
     "eslint-plugin-import": "^2.7.0",
     "eslint-plugin-jsx-a11y": "^6.0.2",
-    "eslint-plugin-react": "^7.4.0",
+    "eslint-plugin-react": "7.10.0",
     "flow-bin": "0.78.0",
     "flow-typed": "2.5.1",
     "jest": "^21.2.1",
@@ -57,6 +57,7 @@
   },
   "dependencies": {
     "@webpack-blocks/webpack2": "^0.4.0",
+    "autobind-decorator": "^2.1.0",
     "axios": "^0.18.0",
     "babel-core": "^6.26.0",
     "babel-loader": "^7.1.2",

+ 2 - 6
src/components/atoms/PasswordValue/PasswordValue.jsx

@@ -54,12 +54,8 @@ type State = {
 }
 @observer
 class PasswordValue extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      show: false,
-    }
+  state = {
+    show: false,
   }
 
   handleShowClick() {

+ 2 - 6
src/components/atoms/Switch/Switch.jsx

@@ -140,12 +140,8 @@ class Switch extends React.Component<Props, State> {
     height: 24,
   }
 
-  constructor() {
-    super()
-
-    this.state = {
-      lastChecked: null,
-    }
+  state = {
+    lastChecked: null,
   }
 
   getLabel() {

+ 10 - 17
src/components/molecules/AutocompleteDropdown/AutocompleteDropdown.jsx

@@ -18,6 +18,7 @@ import React from 'react'
 import { observer } from 'mobx-react'
 import styled, { css } from 'styled-components'
 import ReactDOM from 'react-dom'
+import autobind from 'autobind-decorator'
 
 import AutocompleteInput from '../../atoms/AutocompleteInput'
 import { Tip, updateTipStyle, scrollItemIntoView } from '../Dropdown'
@@ -120,6 +121,13 @@ class AutocompleteDropdown extends React.Component<Props, State> {
     noItemsMessage: 'No results found',
   }
 
+  state = {
+    showDropdownList: false,
+    firstItemHover: false,
+    searchValue: '',
+    filteredItems: [],
+  }
+
   buttonRef: HTMLElement
   listRef: HTMLElement
   listItemsRef: HTMLElement
@@ -129,23 +137,6 @@ class AutocompleteDropdown extends React.Component<Props, State> {
   buttonRect: ClientRect
   itemMouseDown: boolean
 
-  constructor() {
-    super()
-
-    this.state = {
-      showDropdownList: false,
-      firstItemHover: false,
-      searchValue: '',
-      filteredItems: [],
-    }
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
-
-    // $FlowIssue
-    this.handleScroll = this.handleScroll.bind(this)
-  }
-
   componentWillMount() {
     this.setState({
       filteredItems: this.props.items,
@@ -218,6 +209,7 @@ class AutocompleteDropdown extends React.Component<Props, State> {
     })
   }
 
+  @autobind
   handleScroll() {
     if (this.buttonRef) {
       if (DomUtils.isElementInViewport(this.buttonRef, this.scrollableParent)) {
@@ -229,6 +221,7 @@ class AutocompleteDropdown extends React.Component<Props, State> {
     }
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       this.setState({ showDropdownList: false })

+ 8 - 15
src/components/molecules/DatetimePicker/DatetimePicker.jsx

@@ -20,6 +20,7 @@ import { observer } from 'mobx-react'
 import styled, { injectGlobal } from 'styled-components'
 import Datetime from 'react-datetime'
 import moment from 'moment'
+import autobind from 'autobind-decorator'
 
 import DropdownButton from '../../atoms/DropdownButton'
 
@@ -63,26 +64,16 @@ type State = {
 }
 @observer
 class DatetimePicker extends React.Component<Props, State> {
+  state = {
+    showPicker: false,
+    date: null,
+  }
+
   itemMouseDown: boolean
   portalRef: HTMLElement
   buttonRef: HTMLElement
   scrollableParent: HTMLElement
 
-  constructor() {
-    super()
-
-    this.state = {
-      showPicker: false,
-      date: null,
-    }
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
-
-    // $FlowIssue
-    this.handleScroll = this.handleScroll.bind(this)
-  }
-
   componentWillMount() {
     if (this.props.value) {
       this.setState({ date: moment(this.props.value) })
@@ -136,6 +127,7 @@ class DatetimePicker extends React.Component<Props, State> {
     return this.props.isValidDate(currentDate, selectedDate)
   }
 
+  @autobind
   handleScroll() {
     if (this.buttonRef) {
       if (DomUtils.isElementInViewport(this.buttonRef, this.scrollableParent)) {
@@ -146,6 +138,7 @@ class DatetimePicker extends React.Component<Props, State> {
     }
   }
 
+  @autobind
   handlePageClick(e: Event) {
     let path = DomUtils.getEventPath(e)
 

+ 8 - 15
src/components/molecules/Dropdown/Dropdown.jsx

@@ -18,6 +18,7 @@ import React from 'react'
 import { observer } from 'mobx-react'
 import styled, { css } from 'styled-components'
 import ReactDOM from 'react-dom'
+import autobind from 'autobind-decorator'
 
 import DropdownButton from '../../atoms/DropdownButton'
 
@@ -203,6 +204,11 @@ class Dropdown extends React.Component<Props, State> {
     noSelectionMessage: 'Select an item',
   }
 
+  state = {
+    showDropdownList: false,
+    firstItemHover: false,
+  }
+
   buttonRef: HTMLElement
   listRef: HTMLElement
   listItemsRef: HTMLElement
@@ -212,21 +218,6 @@ class Dropdown extends React.Component<Props, State> {
   buttonRect: ClientRect
   itemMouseDown: boolean
 
-  constructor() {
-    super()
-
-    this.state = {
-      showDropdownList: false,
-      firstItemHover: false,
-    }
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
-
-    // $FlowIssue
-    this.handleScroll = this.handleScroll.bind(this)
-  }
-
   componentDidMount() {
     window.addEventListener('mousedown', this.handlePageClick, false)
     if (this.buttonRef) {
@@ -271,6 +262,7 @@ class Dropdown extends React.Component<Props, State> {
     return (item[valueField] != null && item[valueField].toString()) || this.getLabel(item)
   }
 
+  @autobind
   handleScroll() {
     if (this.buttonRef) {
       if (DomUtils.isElementInViewport(this.buttonRef, this.scrollableParent)) {
@@ -282,6 +274,7 @@ class Dropdown extends React.Component<Props, State> {
     }
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       this.setState({ showDropdownList: false })

+ 6 - 11
src/components/molecules/DropdownFilter/DropdownFilter.jsx

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React from 'react'
 import { observer } from 'mobx-react'
 import styled from 'styled-components'
+import autobind from 'autobind-decorator'
 
 import SearchInput from '../SearchInput'
 
@@ -81,19 +82,12 @@ class DropdownFilter extends React.Component<Props, State> {
     searchPlaceholder: 'Filter',
   }
 
-  itemMouseDown: boolean
-
-  constructor() {
-    super()
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
-
-    this.state = {
-      showDropdownList: false,
-    }
+  state = {
+    showDropdownList: false,
   }
 
+  itemMouseDown: boolean
+
   componentDidMount() {
     window.addEventListener('mousedown', this.handlePageClick, false)
   }
@@ -102,6 +96,7 @@ class DropdownFilter extends React.Component<Props, State> {
     window.removeEventListener('mousedown', this.handlePageClick, false)
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       this.setState({ showDropdownList: false })

+ 7 - 12
src/components/molecules/DropdownLink/DropdownLink.jsx

@@ -18,6 +18,7 @@ import React from 'react'
 import { observer } from 'mobx-react'
 import styled, { css } from 'styled-components'
 import ReactDOM from 'react-dom'
+import autobind from 'autobind-decorator'
 
 import SearchInput from '../../molecules/SearchInput'
 
@@ -151,6 +152,11 @@ class DropdownLink extends React.Component<Props, State> {
     noItemsLabel: 'No items',
   }
 
+  state = {
+    showDropdownList: false,
+    searchText: '',
+  }
+
   itemMouseDown: boolean
   labelRef: HTMLElement
   listItemsRef: HTMLElement
@@ -159,18 +165,6 @@ class DropdownLink extends React.Component<Props, State> {
   tipRef: HTMLElement
   searchInputWrapperRef: HTMLElement
 
-  constructor() {
-    super()
-
-    this.state = {
-      showDropdownList: false,
-      searchText: '',
-    }
-
-    const self: any = this
-    self.handlePageClick = this.handlePageClick.bind(this)
-  }
-
   componentDidMount() {
     window.addEventListener('mousedown', this.handlePageClick, false)
     this.setLabelWidth()
@@ -214,6 +208,7 @@ class DropdownLink extends React.Component<Props, State> {
     )
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       this.setState({ showDropdownList: false })

+ 2 - 7
src/components/molecules/Modal/Modal.jsx

@@ -18,6 +18,7 @@ import * as React from 'react'
 import { observer } from 'mobx-react'
 import styled from 'styled-components'
 import Modal from 'react-modal'
+import autobind from 'autobind-decorator'
 
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
@@ -52,13 +53,6 @@ class NewModal extends React.Component<Props> {
   windowScrollY: number
   modalDiv: ?HTMLDivElement
 
-  constructor() {
-    super()
-
-    const self: any = this
-    self.positionModal = this.positionModal.bind(this)
-  }
-
   componentWillMount() {
     if (this.props.componentRef) {
       this.props.componentRef(this)
@@ -122,6 +116,7 @@ class NewModal extends React.Component<Props> {
     window.scroll(0, this.windowScrollY)
   }
 
+  @autobind
   positionModal(scrollOffset: number) {
     // $FlowIssue
     let pageNode = this.modalDiv && this.modalDiv.node.firstChild

+ 6 - 11
src/components/molecules/NewItemDropdown/NewItemDropdown.jsx

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React from 'react'
 import { observer } from 'mobx-react'
 import styled from 'styled-components'
+import autobind from 'autobind-decorator'
 
 import DropdownButton from '../../atoms/DropdownButton'
 
@@ -135,19 +136,12 @@ type State = {
 }
 @observer
 class NewItemDropdown extends React.Component<Props, State> {
-  itemMouseDown: boolean
-
-  constructor() {
-    super()
-
-    this.state = {
-      showDropdownList: false,
-    }
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
+  state = {
+    showDropdownList: false,
   }
 
+  itemMouseDown: boolean
+
   componentDidMount() {
     window.addEventListener('mousedown', this.handlePageClick, false)
   }
@@ -156,6 +150,7 @@ class NewItemDropdown extends React.Component<Props, State> {
     window.removeEventListener('mousedown', this.handlePageClick, false)
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       this.setState({ showDropdownList: false })

+ 4 - 9
src/components/molecules/NotificationDropdown/NotificationDropdown.jsx

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React from 'react'
 import { observer } from 'mobx-react'
 import styled, { css } from 'styled-components'
+import autobind from 'autobind-decorator'
 
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
@@ -179,19 +180,12 @@ type State = {
 const testId = 'notificationDropdown'
 @observer
 class NotificationDropdown extends React.Component<Props, State> {
-  itemMouseDown: boolean
-
-  constructor() {
-    super()
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
-  }
-
   state = {
     showDropdownList: false,
   }
 
+  itemMouseDown: boolean
+
   componentDidMount() {
     window.addEventListener('mousedown', this.handlePageClick, false)
   }
@@ -205,6 +199,7 @@ class NotificationDropdown extends React.Component<Props, State> {
     this.props.onClose()
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       if (this.state.showDropdownList) {

+ 12 - 16
src/components/molecules/SearchInput/SearchInput.jsx

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React from 'react'
 import { observer } from 'mobx-react'
 import styled, { css } from 'styled-components'
+import autobind from 'autobind-decorator'
 
 import SearchButton from '../../atoms/SearchButton'
 import TextInput from '../../atoms/TextInput'
@@ -24,7 +25,7 @@ import StatusIcon from '../../atoms/StatusIcon'
 
 import StyleProps from '../../styleUtils/StyleProps'
 
-const Input = styled(TextInput) `
+const Input = styled(TextInput)`
   padding-left: 32px;
   ${props => props.loading || (props.showClose && props.value) ? 'padding-right: 32px;' : ''}
   width: 50px;
@@ -42,12 +43,12 @@ const Wrapper = styled.div`
   width: ${props => props.open ? props.width : '50px'};
   ${props => props.open ? InputAnimation(props) : ''}
 `
-const SearchButtonStyled = styled(SearchButton) `
+const SearchButtonStyled = styled(SearchButton)`
   position: absolute;
   top: 8px;
   left: 8px;
 `
-const StatusIconStyled = styled(StatusIcon) `
+const StatusIconStyled = styled(StatusIcon)`
   position: absolute;
   right: 8px;
   top: 8px;
@@ -69,7 +70,7 @@ type Props = {
 type State = {
   open: boolean,
   hover?: boolean,
-  focus?: boolean,
+  focus: boolean,
 }
 @observer
 class SearchInput extends React.Component<Props, State> {
@@ -79,21 +80,15 @@ class SearchInput extends React.Component<Props, State> {
     value: '',
   }
 
+  state = {
+    open: false,
+    value: '',
+    focus: false,
+  }
+
   input: HTMLElement
   itemMouseDown: boolean
 
-  constructor() {
-    super()
-
-    this.state = {
-      open: false,
-      value: '',
-    }
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
-  }
-
   componentDidMount() {
     window.addEventListener('mousedown', this.handlePageClick, false)
 
@@ -104,6 +99,7 @@ class SearchInput extends React.Component<Props, State> {
     window.removeEventListener('mousedown', this.handlePageClick, false)
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       this.setState({ open: false })

+ 2 - 6
src/components/molecules/SideMenu/SideMenu.jsx

@@ -91,12 +91,8 @@ type State = {
 }
 @observer
 class SideMenu extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      open: false,
-    }
+  state = {
+    open: false,
   }
 
   handleHamburgerClick() {

+ 6 - 11
src/components/molecules/UserDropdown/UserDropdown.jsx

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React from 'react'
 import { observer } from 'mobx-react'
 import styled, { css } from 'styled-components'
+import autobind from 'autobind-decorator'
 
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
@@ -114,19 +115,12 @@ type State = {
 }
 @observer
 class UserDropdown extends React.Component<Props, State> {
-  itemMouseDown: boolean
-
-  constructor() {
-    super()
-
-    this.state = {
-      showDropdownList: false,
-    }
-
-    // $FlowIssue
-    this.handlePageClick = this.handlePageClick.bind(this)
+  state = {
+    showDropdownList: false,
   }
 
+  itemMouseDown: boolean
+
   componentDidMount() {
     window.addEventListener('mousedown', this.handlePageClick, false)
   }
@@ -143,6 +137,7 @@ class UserDropdown extends React.Component<Props, State> {
     this.setState({ showDropdownList: false })
   }
 
+  @autobind
   handlePageClick() {
     if (!this.itemMouseDown) {
       this.setState({ showDropdownList: false })

+ 5 - 9
src/components/organisms/AssessmentMigrationOptions/AssessmentMigrationOptions.jsx

@@ -40,7 +40,7 @@ const Image = styled.div`
 const Fields = styled.div`
   margin-top: 64px;
 `
-const WizardOptionsFieldStyled = styled(WizardOptionsField) `
+const WizardOptionsFieldStyled = styled(WizardOptionsField)`
   width: 319px;
   justify-content: space-between;
   margin-bottom: 32px;
@@ -91,14 +91,10 @@ type State = {
 }
 @observer
 class AssessmentMigrationOptions extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      generalFields: [...generalFields],
-      migrationFields: [...migrationFields],
-      replicaFields: [...replicaFields],
-    }
+  state = {
+    generalFields: [...generalFields],
+    migrationFields: [...migrationFields],
+    replicaFields: [...replicaFields],
   }
 
   handleValueChange(field: Field, value: any) {

+ 9 - 13
src/components/organisms/Endpoint/Endpoint.jsx

@@ -128,6 +128,14 @@ class Endpoint extends React.Component<Props, State> {
     cancelButtonText: 'Cancel',
   }
 
+  state = {
+    invalidFields: [],
+    validating: false,
+    showErrorMessage: false,
+    endpoint: null,
+    isNew: null,
+  }
+
   scrollableRef: HTMLElement
   closeTimeout: TimeoutID
   contentPluginRef: DefaultContentPlugin
@@ -135,18 +143,6 @@ class Endpoint extends React.Component<Props, State> {
   providerStoreObserver: () => void
   endpointValidationObserver: () => void
 
-  constructor() {
-    super()
-
-    this.state = {
-      invalidFields: [],
-      validating: false,
-      showErrorMessage: false,
-      endpoint: null,
-      isNew: null,
-    }
-  }
-
   componentWillMount() {
     this.componentWillReceiveProps(this.props)
     this.providerStoreObserver = observe(providerStore, 'connectionInfoSchema', () => {
@@ -222,7 +218,7 @@ class Endpoint extends React.Component<Props, State> {
   }
 
   handleFieldsChange(items: { field: Field, value: any }[]) {
-    let endpoint: EndpointType = { ...this.state.endpoint }
+    let endpoint: any = { ...this.state.endpoint }
 
     items.forEach(item => {
       endpoint[item.field.name] = item.value

+ 2 - 6
src/components/organisms/Executions/Executions.jsx

@@ -98,12 +98,8 @@ type State = {
 }
 @observer
 class Executions extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      selectedExecution: null,
-    }
+  state = {
+    selectedExecution: null,
   }
 
   componentWillMount() {

+ 7 - 10
src/components/organisms/FilterList/FilterList.jsx

@@ -49,19 +49,16 @@ type State = {
   filterStatus: string,
   filterText: string,
   selectedItems: any[],
-  selectAllSelected?: boolean,
+  selectAllSelected: boolean,
 }
 @observer
 class FilterList extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      items: [],
-      filterStatus: 'all',
-      filterText: '',
-      selectedItems: [],
-    }
+  state = {
+    items: [],
+    filterStatus: 'all',
+    filterText: '',
+    selectedItems: [],
+    selectAllSelected: false,
   }
 
   componentWillMount() {

+ 3 - 7
src/components/organisms/LoginForm/LoginForm.jsx

@@ -99,13 +99,9 @@ class LoginForm extends React.Component<Props, State> {
     className: '',
   }
 
-  constructor() {
-    super()
-
-    this.state = {
-      username: '',
-      password: '',
-    }
+  state = {
+    username: '',
+    password: '',
   }
 
   handleUsernameChange(username: string) {

+ 1 - 6
src/components/organisms/Notifications/Notifications.jsx

@@ -33,13 +33,8 @@ const Wrapper = styled.div``
 
 @observer
 class Notifications extends React.Component<{}> {
-  notificationsCount: number
   notificationSystem: NotificationSystem
-
-  constructor() {
-    super()
-    this.notificationsCount = 0
-  }
+  notificationsCount = 0
 
   componentDidMount() {
     observe(notificationStore.alerts, change => {

+ 9 - 12
src/components/organisms/PageHeader/PageHeader.jsx

@@ -71,24 +71,21 @@ type State = {
   showEndpointModal: boolean,
   showUserModal: boolean,
   showProjectModal: boolean,
-  providerType?: string,
+  providerType: ?string,
 }
 @observer
 class PageHeader extends React.Component<Props, State> {
+  state = {
+    showChooseProviderModal: false,
+    showEndpointModal: false,
+    showUserModal: false,
+    showProjectModal: false,
+    providerType: null,
+  }
+
   pollTimeout: TimeoutID
   stopPolling: boolean
 
-  constructor() {
-    super()
-
-    this.state = {
-      showChooseProviderModal: false,
-      showEndpointModal: false,
-      showUserModal: false,
-      showProjectModal: false,
-    }
-  }
-
   componentWillMount() {
     this.stopPolling = false
     this.pollData()

+ 2 - 2
src/components/organisms/ProjectDetailsContent/ProjectDetailsContent.jsx

@@ -114,13 +114,13 @@ type State = {
 const testName = 'pdContent'
 @observer
 class ProjectDetailsContent extends React.Component<Props, State> {
-  selectedUser: ?User
-
   state = {
     showRemoveUserAlert: false,
     showDeleteProjectAlert: false,
   }
 
+  selectedUser: ?User
+
   handleRemoveUserAction(user: User) {
     this.selectedUser = user
     this.setState({ showRemoveUserAlert: true })

+ 2 - 6
src/components/organisms/ReplicaDetailsContent/ReplicaDetailsContent.jsx

@@ -91,12 +91,8 @@ type State = {
 }
 @observer
 class ReplicaDetailsContent extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      timezone: 'local',
-    }
+  state = {
+    timezone: 'local',
   }
 
   getLastExecution() {

+ 2 - 6
src/components/organisms/ReplicaExecutionOptions/ReplicaExecutionOptions.jsx

@@ -69,12 +69,8 @@ class ReplicaExecutionOptions extends React.Component<Props, State> {
     executionLabel: 'Execute',
   }
 
-  constructor() {
-    super()
-
-    this.state = {
-      fields: [...executionOptions],
-    }
+  state = {
+    fields: [...executionOptions],
   }
 
   componentDidMount() {

+ 18 - 21
src/components/organisms/ReplicaMigrationOptions/ReplicaMigrationOptions.jsx

@@ -50,7 +50,7 @@ const Buttons = styled.div`
   justify-content: space-between;
   width: 100%;
 `
-const WizardOptionsFieldStyled = styled(WizardOptionsField) `
+const WizardOptionsFieldStyled = styled(WizardOptionsField)`
   width: 319px;
   justify-content: space-between;
   margin-bottom: 32px;
@@ -63,28 +63,25 @@ type Props = {
 type State = {
   fields: Field[],
 }
+let defaultFields: Field[] = [
+  {
+    name: 'clone_disks',
+    type: 'strict-boolean',
+    value: true,
+  },
+  {
+    name: 'force',
+    type: 'strict-boolean',
+  },
+  {
+    name: 'skip_os_morphing',
+    type: 'strict-boolean',
+  },
+]
 @observer
 class ReplicaMigrationOptions extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      fields: [
-        {
-          name: 'clone_disks',
-          type: 'strict-boolean',
-          value: true,
-        },
-        {
-          name: 'force',
-          type: 'strict-boolean',
-        },
-        {
-          name: 'skip_os_morphing',
-          type: 'strict-boolean',
-        },
-      ],
-    }
+  state = {
+    fields: defaultFields,
   }
 
   componentDidMount() {

+ 5 - 9
src/components/organisms/Schedule/Schedule.jsx

@@ -134,15 +134,11 @@ class Schedule extends React.Component<Props, State> {
     unsavedSchedules: [],
   }
 
-  constructor() {
-    super()
-
-    this.state = {
-      showOptionsModal: false,
-      showDeleteConfirmation: false,
-      selectedSchedule: null,
-      executionOptions: null,
-    }
+  state = {
+    showOptionsModal: false,
+    showDeleteConfirmation: false,
+    selectedSchedule: null,
+    executionOptions: null,
   }
 
   handleDeleteClick(selectedSchedule: ScheduleType) {

+ 4 - 8
src/components/organisms/Tasks/Tasks.jsx

@@ -51,16 +51,12 @@ type State = {
 }
 @observer
 class Tasks extends React.Component<Props, State> {
-  dragStartPosition: ?{ x: number, y: number }
-
-  constructor() {
-    super()
-
-    this.state = {
-      openedItems: [],
-    }
+  state = {
+    openedItems: [],
   }
 
+  dragStartPosition: ?{ x: number, y: number }
+
   componentWillMount() {
     this.componentWillReceiveProps(this.props)
   }

+ 5 - 9
src/components/organisms/WizardInstances/WizardInstances.jsx

@@ -62,7 +62,7 @@ const InstanceContent = styled.div`
     background: ${Palette.grayscale[1]};
   }
 `
-const CheckboxStyled = styled(Checkbox) `
+const CheckboxStyled = styled(Checkbox)`
   opacity: 0;
   transition: all ${StyleProps.animations.swift};
 `
@@ -201,16 +201,12 @@ type State = {
 }
 @observer
 class WizardInstances extends React.Component<Props, State> {
-  timeout: TimeoutID
-
-  constructor() {
-    super()
-
-    this.state = {
-      searchText: '',
-    }
+  state = {
+    searchText: '',
   }
 
+  timeout: TimeoutID
+
   handleSeachInputChange(searchText: string) {
     this.setState({ searchText })
 

+ 2 - 7
src/components/organisms/WizardOptions/WizardOptions.jsx

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React from 'react'
 import styled from 'styled-components'
 import { observer } from 'mobx-react'
+import autobind from 'autobind-decorator'
 
 import Tooltip from '../../atoms/Tooltip'
 import StyleProps from '../../styleUtils/StyleProps'
@@ -66,13 +67,6 @@ type Props = {
 }
 @observer
 class WizardOptions extends React.Component<Props> {
-  constructor() {
-    super()
-
-    // $FlowIssue
-    this.handleResize = this.handleResize.bind(this)
-  }
-
   componentDidMount() {
     window.addEventListener('resize', this.handleResize)
   }
@@ -124,6 +118,7 @@ class WizardOptions extends React.Component<Props> {
     return fieldsSchema
   }
 
+  @autobind
   handleResize() {
     this.setState({})
   }

+ 3 - 7
src/components/organisms/WizardPageContent/WizardPageContent.jsx

@@ -125,13 +125,9 @@ type State = {
 const testName = 'wpContent'
 @observer
 class WizardPageContent extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      useAdvancedOptions: false,
-      timezone: 'local',
-    }
+  state = {
+    useAdvancedOptions: false,
+    timezone: 'local',
   }
 
   componentDidMount() {

+ 8 - 12
src/components/pages/AssessmentDetailsPage/AssessmentDetailsPage.jsx

@@ -57,18 +57,14 @@ type State = {
 }
 @observer
 class AssessmentDetailsPage extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      sourceEndpoint: null,
-      selectedVms: [],
-      selectedNetworks: [],
-      showMigrationOptions: false,
-      executeButtonDisabled: false,
-      vmSizes: {},
-      vmSearchValue: '',
-    }
+  state = {
+    sourceEndpoint: null,
+    selectedVms: [],
+    selectedNetworks: [],
+    showMigrationOptions: false,
+    executeButtonDisabled: false,
+    vmSizes: {},
+    vmSearchValue: '',
   }
 
   componentWillMount() {

+ 3 - 3
src/components/pages/AssessmentsPage/AssessmentsPage.jsx

@@ -41,13 +41,13 @@ type Props = {}
 type State = { modalIsOpen: boolean }
 @observer
 class AssessmentsPage extends React.Component<Props, State> {
-  disablePolling: boolean
-  pollTimeout: TimeoutID
-
   state = {
     modalIsOpen: false,
   }
 
+  disablePolling: boolean
+  pollTimeout: TimeoutID
+
   componentWillMount() {
     document.title = 'Coriolis Planning'
 

+ 7 - 11
src/components/pages/EndpointDetailsPage/EndpointDetailsPage.jsx

@@ -51,17 +51,13 @@ type State = {
 }
 @observer
 class EndpointDetailsPage extends React.Component<Props, State> {
-  constructor() {
-    super()
-
-    this.state = {
-      showDeleteEndpointConfirmation: false,
-      showValidationModal: false,
-      showEndpointModal: false,
-      showEndpointInUseModal: false,
-      showEndpointInUseLoadingModal: false,
-      endpointUsage: { replicas: [], migrations: [] },
-    }
+  state = {
+    showDeleteEndpointConfirmation: false,
+    showValidationModal: false,
+    showEndpointModal: false,
+    showEndpointInUseModal: false,
+    showEndpointInUseLoadingModal: false,
+    endpointUsage: { replicas: [], migrations: [] },
   }
 
   componentDidMount() {

+ 12 - 16
src/components/pages/EndpointsPage/EndpointsPage.jsx

@@ -63,25 +63,21 @@ type State = {
 }
 @observer
 class EndpointsPage extends React.Component<{}, State> {
+  state = {
+    showDeleteEndpointsConfirmation: false,
+    confirmationItems: null,
+    showChooseProviderModal: false,
+    showEndpointModal: false,
+    providerType: null,
+    showEndpointsInUseModal: false,
+    modalIsOpen: false,
+    showDuplicateModal: false,
+    duplicating: false,
+  }
+
   pollTimeout: TimeoutID
   stopPolling: boolean
 
-  constructor() {
-    super()
-
-    this.state = {
-      showDeleteEndpointsConfirmation: false,
-      confirmationItems: null,
-      showChooseProviderModal: false,
-      showEndpointModal: false,
-      providerType: null,
-      showEndpointsInUseModal: false,
-      modalIsOpen: false,
-      showDuplicateModal: false,
-      duplicating: false,
-    }
-  }
-
   componentDidMount() {
     document.title = 'Coriolis Endpoints'
 

+ 5 - 9
src/components/pages/MigrationDetailsPage/MigrationDetailsPage.jsx

@@ -43,17 +43,13 @@ type State = {
 }
 @observer
 class MigrationDetailsPage extends React.Component<Props, State> {
-  pollInterval: IntervalID
-
-  constructor() {
-    super()
-
-    this.state = {
-      showDeleteMigrationConfirmation: false,
-      showCancelConfirmation: false,
-    }
+  state = {
+    showDeleteMigrationConfirmation: false,
+    showCancelConfirmation: false,
   }
 
+  pollInterval: IntervalID
+
   componentDidMount() {
     document.title = 'Migration Details'
 

+ 7 - 11
src/components/pages/MigrationsPage/MigrationsPage.jsx

@@ -50,20 +50,16 @@ type State = {
 }
 @observer
 class MigrationsPage extends React.Component<{}, State> {
+  state = {
+    showDeleteMigrationConfirmation: false,
+    showCancelMigrationConfirmation: false,
+    confirmationItems: null,
+    modalIsOpen: false,
+  }
+
   pollTimeout: TimeoutID
   stopPolling: boolean
 
-  constructor() {
-    super()
-
-    this.state = {
-      showDeleteMigrationConfirmation: false,
-      showCancelMigrationConfirmation: false,
-      confirmationItems: null,
-      modalIsOpen: false,
-    }
-  }
-
   componentDidMount() {
     document.title = 'Coriolis Migrations'
 

+ 3 - 3
src/components/pages/ProjectsPage/ProjectsPage.jsx

@@ -37,13 +37,13 @@ type State = {
 }
 @observer
 class ProjectsPage extends React.Component<{}, State> {
-  pollTimeout: TimeoutID
-  stopPolling: boolean
-
   state = {
     modalIsOpen: false,
   }
 
+  pollTimeout: TimeoutID
+  stopPolling: boolean
+
   componentDidMount() {
     document.title = 'Projects'
 

+ 10 - 14
src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.jsx

@@ -56,22 +56,18 @@ type State = {
 }
 @observer
 class ReplicaDetailsPage extends React.Component<Props, State> {
-  pollTimeout: TimeoutID
-
-  constructor() {
-    super()
-
-    this.state = {
-      showOptionsModal: false,
-      showMigrationModal: false,
-      showDeleteExecutionConfirmation: false,
-      showDeleteReplicaConfirmation: false,
-      showDeleteReplicaDisksConfirmation: false,
-      confirmationItem: null,
-      showCancelConfirmation: false,
-    }
+  state = {
+    showOptionsModal: false,
+    showMigrationModal: false,
+    showDeleteExecutionConfirmation: false,
+    showDeleteReplicaConfirmation: false,
+    showDeleteReplicaDisksConfirmation: false,
+    confirmationItem: null,
+    showCancelConfirmation: false,
   }
 
+  pollTimeout: TimeoutID
+
   componentDidMount() {
     document.title = 'Replica Details'
 

+ 6 - 10
src/components/pages/ReplicasPage/ReplicasPage.jsx

@@ -49,19 +49,15 @@ type State = {
 }
 @observer
 class ReplicasPage extends React.Component<{}, State> {
+  state = {
+    showDeleteReplicaConfirmation: false,
+    confirmationItems: null,
+    modalIsOpen: false,
+  }
+
   pollTimeout: TimeoutID
   stopPolling: boolean
 
-  constructor() {
-    super()
-
-    this.state = {
-      showDeleteReplicaConfirmation: false,
-      confirmationItems: null,
-      modalIsOpen: false,
-    }
-  }
-
   componentDidMount() {
     document.title = 'Coriolis Replicas'
 

+ 3 - 3
src/components/pages/UsersPage/UsersPage.jsx

@@ -37,13 +37,13 @@ type State = {
 }
 @observer
 class UsersPage extends React.Component<{}, State> {
-  pollTimeout: TimeoutID
-  stopPolling: boolean
-
   state = {
     modalIsOpen: false,
   }
 
+  pollTimeout: TimeoutID
+  stopPolling: boolean
+
   componentDidMount() {
     document.title = 'Users'
 

+ 8 - 11
src/components/pages/WizardPage/WizardPage.jsx

@@ -54,23 +54,20 @@ type State = {
   type: WizardType,
   showNewEndpointModal: boolean,
   nextButtonDisabled: boolean,
-  newEndpointType?: string,
+  newEndpointType: ?string,
   newEndpointFromSource?: boolean,
 }
 @observer
 class WizardPage extends React.Component<Props, State> {
-  contentRef: WizardPageContent
-
-  constructor() {
-    super()
-
-    this.state = {
-      type: 'migration',
-      showNewEndpointModal: false,
-      nextButtonDisabled: false,
-    }
+  state = {
+    type: 'migration',
+    showNewEndpointModal: false,
+    nextButtonDisabled: false,
+    newEndpointType: null,
   }
 
+  contentRef: WizardPageContent
+
   componentWillMount() {
     this.initializeState()
   }

+ 5 - 9
src/plugins/endpoint/azure/ContentPlugin.jsx

@@ -82,18 +82,14 @@ type State = {
   showPasteInput: boolean,
 }
 class ContentPlugin extends React.Component<Props, State> {
+  state = {
+    jsonConfig: '',
+    showPasteInput: false,
+  }
+
   cloudProfileChanged: boolean
   lastBlurValue: string
 
-  constructor() {
-    super()
-
-    this.state = {
-      jsonConfig: '',
-      showPasteInput: false,
-    }
-  }
-
   componentDidMount() {
     this.props.onRef(this)
   }

+ 5 - 8
src/plugins/endpoint/openstack/ContentPlugin.jsx

@@ -21,7 +21,7 @@ import ToggleButtonBar from '../../../components/atoms/ToggleButtonBar'
 import type { Field } from '../../../types/Field'
 import { Wrapper, Fields, FieldStyled, Row } from '../default/ContentPlugin'
 
-const ToggleButtonBarStyled = styled(ToggleButtonBar) `
+const ToggleButtonBarStyled = styled(ToggleButtonBar)`
   margin-top: 16px;
 `
 
@@ -42,13 +42,12 @@ type State = {
   useAdvancedOptions: boolean,
 }
 class ContentPlugin extends React.Component<Props, State> {
-  constructor() {
-    super()
-    this.state = {
-      useAdvancedOptions: false,
-    }
+  state = {
+    useAdvancedOptions: false,
   }
 
+  previouslySelectedChoices: string[] = []
+
   componentDidMount() {
     this.props.onRef(this)
   }
@@ -96,8 +95,6 @@ class ContentPlugin extends React.Component<Props, State> {
     this.setState({ useAdvancedOptions })
   }
 
-  previouslySelectedChoices: string[] = []
-
   findInvalidFields = () => {
     let inputChoices = ['user_domain', 'project_domain']
 

+ 31 - 8
yarn.lock

@@ -621,6 +621,10 @@ asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
 
+autobind-decorator@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-2.1.0.tgz#4451240dbfeff46361c506575a63ed40f0e5bc68"
+
 autoprefixer@^6.3.1:
   version "6.7.7"
   resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
@@ -2883,6 +2887,12 @@ doctrine@^2.0.0:
     esutils "^2.0.2"
     isarray "^1.0.0"
 
+doctrine@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+  dependencies:
+    esutils "^2.0.2"
+
 dom-converter@~0.1:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b"
@@ -3273,14 +3283,14 @@ eslint-plugin-jsx-a11y@^6.0.2:
     emoji-regex "^6.1.0"
     jsx-ast-utils "^1.4.0"
 
-eslint-plugin-react@^7.4.0:
-  version "7.4.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz#300a95861b9729c087d362dd64abcc351a74364a"
+eslint-plugin-react@7.10.0:
+  version "7.10.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.10.0.tgz#af5c1fef31c4704db02098f9be18202993828b50"
   dependencies:
-    doctrine "^2.0.0"
-    has "^1.0.1"
-    jsx-ast-utils "^2.0.0"
-    prop-types "^15.5.10"
+    doctrine "^2.1.0"
+    has "^1.0.3"
+    jsx-ast-utils "^2.0.1"
+    prop-types "^15.6.2"
 
 eslint-restricted-globals@^0.1.1:
   version "0.1.1"
@@ -4088,6 +4098,12 @@ has@^1.0.1:
   dependencies:
     function-bind "^1.0.2"
 
+has@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+  dependencies:
+    function-bind "^1.1.1"
+
 hash-base@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
@@ -5080,7 +5096,7 @@ jsx-ast-utils@^1.4.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
 
-jsx-ast-utils@^2.0.0:
+jsx-ast-utils@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
   dependencies:
@@ -6601,6 +6617,13 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7,
     loose-envify "^1.3.1"
     object-assign "^4.1.1"
 
+prop-types@^15.6.2:
+  version "15.6.2"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
+  dependencies:
+    loose-envify "^1.3.1"
+    object-assign "^4.1.1"
+
 proxy-addr@~2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"