Example: Component Class

// import named exports like PropTypes and Component, instead of React.PropTypes
// makes it more clear what is used, and shorter to use
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// place all module imports above any relative imports
import { connect } from 'react-redux';
import { componentClassNameProp } from 'common/src/app/util/componentClassNameUtils';

// import css at the bottom of all the imports (with a blank line above it)
// import './example.scss';

// when you connect this component to redux, export this unconnected component
// by name, so it can be imported by unit tests
export class Example extends Component {
  constructor(props) {
    super(props);

    // define your refs at the top, so it's clear what refs are available
    this.fooRef = null;

    // define state in the constructor, because sometimes you need info from the props
    this.state = {
      foo: 'bar',
      // When using props in the state, you should always use componentWillReceiveProps
      // to update the state, or else consecutive renders will not use the updated props
      // If possible, you should move this logic to the render function to prevent
      // duplication of code: https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
      // Also, never use `this.props` here, as it doesn't work on older IE versions
      bar: props.size,
    };
  }

  // TODO: event handlers

  componentWillMount() {

  }

  componentDidMount() {
    // start using the DOM from this point
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      bar: nextProps.bar,
    });
  }

  shouldComponentUpdate(nextProps, nextState) {
    // be careful using this method
    // when you introduce new props/state, and you forget to update this check,
    // your component won't update
    // best is to use pure props
    return this.props.size !== nextProps.size || this.state.bar !== nextState.bar;
  }

  componentDidUpdate() {
    // not called for the first render
    // do DOM calculation after render here
  }

  componentWillUnmount() {

  }

  componentDidUnmount() {
    // clean up your stuff here
  }

  // TODO: render helper methods

  render() {
    // prefer to move logic at the top of the render method,
    // and only use simple variables in the JSX part

    // destructuring from state and props is a good way to show what variables you are using here
    const { size, children } = this.props;
    const { foo } = this.state;

    // always use the ref-function, the `this.ref.foo` usage is legacy
    // https://facebook.github.io/react/docs/more-about-refs.html
    return (
      <div ref={ref => { this.fooRef = ref; }}
           foo={foo}
           {...componentClassNameProp(this, null, `size-${size}`)}>
        {children}
      </div>
    );
  }
}

Example.propTypes = {
  /** Size of the buttons */
  size: PropTypes.oneOf([
    'large', 'medium', 'small',
  ]),
  children: PropTypes.node.isRequired,
};

Example.defaultProps = {
  size: 'medium',
};

const ConnectedExample = connect(
  // connect stuff here, see component-class-connect.js example for more
)(Example);

export default ConnectedExample;