origin article

Initializing State

  1. export default class ProfileContainer extends Component {
  2. state = { expanded: false }

propTypes and defaultProps

export default class ProfileContainer extends Component {
  state = { expanded: false }

  static propTypes = {
    model: object.isRequired,
    title: string
  }

  static defaultProps = {
    model: {
      id: 0
    },
    title: 'Your Name'
  }

Methods

this approach is cleaner and easier, maintaining the correct context automatically via the ES6 arrow function

export default class ProfileContainer extends Component {
  state = { expanded: false }

  static propTypes = {
    model: object.isRequired,
    title: string
  }

  static defaultProps = {
    model: {
      id: 0
    },
    title: 'Your Name'
  }
  handleSubmit = (e) => {
    e.preventDefault()
    this.props.model.save()
  }

  handleNameChange = (e) => {
    this.props.model.changeName(e.target.value)
  }

  handleExpand = (e) => {
    e.preventDefault()
    this.setState({ expanded: !this.state.expanded })
  }

Passing setState a Function

setState — it’s actually asynchronous. React batches state changes for performance reasons, so the state may not change immediately after setState is called

That means you should not rely on the current state when calling setState — since you can’t be sure what that state will be!

Here’s the solution — pass a function to setState, with the previous state as an argument.

this.setState({ expanded: !this.state.expanded })
// below is best practice
this.setState(prevState => ({ expanded: !prevState.expanded }))

Destructuring Props

Components with many props should have each prop on a newline, like above.

render() {
    const {
      model,
      title
    } = this.props
    return ( 
     ....
    )
  }

Decorators

Decorators are flexible and readable way of modifying component functionality.
you can decorate your class components like so — which is the same as passing the component into a function.

Closures

Avoid passing new closures to subcomponents, like so:

          <input
            type="text"
            value={model.name}
            // onChange={(e) => { model.name = e.target.value }}
            // ^ Not this. Use the below:
            onChange={this.handleChange}
            placeholder="Your Name"/>

Here’s why: every time the parent component renders, a new function is created and passed to the input

If the input were a React component, this would automatically trigger it to re-render, regardless of whether its other props have actually changed.

Functional Components

These components have no state and no methods. They’re pure, and easy to reason about. Use them as often as possible.

ExpandableForm.propTypes = {
  onSubmit: func.isRequired,
  expanded: bool,
  onExpand: func.isRequired
}

// way 1
function ExpandableForm(props) {
  const formStyle = props.expanded ? {height: 'auto'} : {height: 0}
  return (
    <form style={formStyle} onSubmit={props.onSubmit}>
      {props.children}
      <button onClick={props.onExpand}>Expand</button>
    </form>
  )
}

// way 2  but avoid use this way
function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
  const formStyle = expanded ? {height: 'auto'} : {height: 0}
  return (
    <form style={formStyle} onSubmit={onSubmit}>
      {children}
      <button onClick={onExpand}>Expand</button>
    </form>
  )
}

Avoid the following ES6 syntax:

const ExpandableForm = ({ onExpand, expanded, children }) => {

Looks very modern, but the function here is actually unnamed.
1、This lack of name will not be a problem if your Babel is set up correctly — but if it’s not, any errors will show up as occurring in <> which is terrible for debugging.
2、Unnamed functions can also cause problems with Jest, a React testing library. Due to the potential for difficult-to-understand bugs (and the lack of real benefit) we recommend using function instead of const

Wrapping

Since you can’t use decorators with functional components, you simply pass it the function in as an argument:

export default observer(ExpandableForm)