React components went the long way from React.createClass through ES2015 powered React.Component to React.PureComponent and stateless functional components. I really enjoy the idea that we don’t have to “hack” the language anymore, at least not that much as we used to. The progress in this department is quite clear and brings not always obvious benefits. Using constructs built into the language/transpiler instead of relying on framework’s factory functions or constructors accepting huge configuration objects future proofs your code.

With Babel or TypeScript future is already here or comes down to your risk aversion for transpiling your code with stage-N preset. Stage 2 is a proposal likely to be included in the new standard. It brings class fields and static properties. React components can greatly benefit from them, both when it comes to performance and reducing noise in your code.

Initializing state

As you should prefer stateless components over classes, sometimes you need to persist some state. To initialize state in your React component we are used to doing this in the constructor.

// BEFORE
import React, { Component } from "react";

class Counter extends Component {
  constructor(props) {
    super(props);

    this.state = { counter: 0 };
  }

  ...
}

Initializing state inside of the constructor comes with an overhead of calling super and remembering about props which are React’s abstractions which leak here a little. We can initialize state directly as a class property.

// AFTER
import React, { Component } from "react";

class Counter extends Component {
  state = { counter: 0 };

  ...
}

Although it reduces noise in your code it comes with the limitation that you are no longer able to initialize component’s state with props.

Bounded methods

It’s common for event handlers to modify state. To do that handler has to be called in the component’s context which isn’t going to happen by default. The common source of the performance problems is passing event handlers with a bounded state with arrow function or calling .bind(this) on the event handler inside the render() call. Each of those techniques causes creation of new function inside the render killing PureComponent/PureRenderMixin optimizations. The right way to approach this problem is binding your event handler inside of the component’s constructor.

// BEFORE
import React, { Component } from "react";

class Counter extends Component {
  state = { counter: 0 };

  constructor(props) {
    super(props);

    this.onIncrementClick = this.onIncrementClick.bind(this);
  }

  onIncrementClick() {
    this.setState(this.increment);
  }

  increment(state) {
    return { ...state, counter: state.counter + 1 };
  }

  render() {
    return <button onClick={this.onIncrementClick}>{this.state.counter}</button>;
  }
}

We can benefit from the fact that arrow function preserves context in which was defined and set handler directly as a class field.

// AFTER
import React, { Component } from "react";

class Counter extends Component {
  state = { counter: 0 };

  onIncrementClick = () => {
    this.setState(this.increment);
  }

  increment(state) {
    return { ...state, counter: state.counter + 1 };
  }

  render() {
    return <button onClick={this.onIncrementClick}>{this.state.counter}</button>;
  }
}

Static fields

There are three common cases where static fields shine when it comes to React components. It’s setting propsTypes, defaultProps and childContextTypes. We are used to setting them outside of the class. It often makes you scroll to the very bottom of the file to learn about props which component accepts.

// BEFORE
import React, { Component, PropTypes } from "react";

class Counter extends Component {
  ...
}

Counter.propTypes = {
  step: PropTypes.number.isRequired,
};

Defining props as static fields allows you to keep them inside of the class and benefit from a common convention of keeping statics at the top of the class.

// AFTER
import React, { Component, PropTypes } from "react";

class Counter extends Component {
  static propTypes = {
    step: PropTypes.number.isRequired,
  }
}

What’s next

I can only guess but I’m sure we are going to discover great use cases for await/async and popularize partial application with rest parameters. No matter what’ll come first, it’s good we’re ready.