A sunrise (or sunset, i don't know)

How to create components

  • Kristofer Giltvedt Selbekk

React celebrated its fifth birthday in 2018. For a front-end framework, that's a pretty long run! Through those 5 years, new features have been added, older ones have been deprecated, and APIs have changed drastically.

This post will introduce you to all the ways to make a component today, and how you should write the ones of tomorrow.

It started as a factory 🏭

When Jordan Walke introduced the world to React back in 2013, components were created with a factory function - React.createClass. You passed in an object containing your render method (and other methods), and the returned value was your component! Here's how it looked:

const Gift = React.createClass({
    getInitialState: function() {
        return {
            delivered: false,
        };
    },
    handleDeliverClick: function(e) {
        this.setState({ delivered: true });
    },
    render: function() {
        return (
            <div>
                <p>{this.props.name}</p>
                <p>{this.state.delivered ? 'Delivered' : 'On its way'}</p>
                {!this.state.delivered && (
                    <button onClick={this.handleDeliverClick}>Deliver</button>
                )}
            </div>
        );
    },
});

You would set the initial state by calling the getInitialState method, and you didn't have to bind event handlers to get the correct this value.

This way of creating components was deprecated in React 15, since ES6 classes provided an easier API - but it's still available through the create-react-class NPM package.

Then came classes 🎓

React was released in 2013 - years before the concept of classes was a part of the JavaScript language. When ES2015 finally landed, however, the React team was quick to adopt classes as their favored way of creating components. In January 2015, the 0.13.0 beta release added them!

Class based components (or class components, for short) should look familiar to you:

class Gift extends React.Component {
    constructor(props) {
        super(props);

        this.handleDeliverClick = this.handleDeliverClick.bind(this);

        this.state = {
            delivered: false,
        };
    }

    handleDeliverClick() {
        this.setState({ delivered: true });
    }

    render() {
        return (
            <div>
                <p>{this.props.name}</p>
                <p>{this.state.delivered ? 'Delivered' : 'On its way'}</p>
                {!this.state.delivered && (
                    <button onClick={this.handleDeliverClick}>Deliver</button>
                )}
            </div>
        );
    }
}

Since we're now living in the future, we now have class instance field declarations as well, so the same code could now be written like this:

class Gift extends React.Component {
    state = { delivered: false };
    handleDeliverClick = () => this.setState({ delivered: true });
    render() {
        return (
            <div>
                <p>{this.props.name}</p>
                <p>{this.state.delivered ? 'Delivered' : 'On its way'}</p>
                {!this.state.delivered && (
                    <button onClick={this.handleDeliverClick}>Deliver</button>
                )}
            </div>
        );
    }
}

The JavaScript classes made React a lot less magic - but also a tiny bit more verbose. The fancy auto-binding feature was gone, and so was the mixins. There was a lot of angry blog posts about this at the time, but I think this move was pretty nice nonetheless.

Functions Functions Functions 💥

Whether you were using classes or React.createClass to make your components, it was still a pretty verbose procedure. In October 2015, the React team introduced what they called "stateless function components". This approach for creating components were tailored for the very common case when you didn't need state or lifecycle methods.

Here's a stateless version of our sample component, created as a function:

function Gift(props) {
    return (
        <div>
            <p>{props.name}</p>
            <p>{props.delivered ? 'Delivered' : 'On its way'}</p>
            {!props.delivered && <button onClick={props.handleDeliverClick}>Deliver</button>}
        </div>
    );
}

If you want, you can use an arrow function instead:

const Gift = props => (
    <div>
        <p>{props.name}</p>
        <p>{props.delivered ? 'Delivered' : 'On its way'}</p>
        {!props.delivered && <button onClick={props.handleDeliverClick}>Deliver</button>}
    </div>
);

Note that the click handler and state now needs to live outside of our component.

This is probably the most used way of creating components. Function components are less clunky than their class alternatives, and are powerful enough for most usecases.

And then there were hooks. 🎣

In a few months, React is going to release the concept of Hooks. Markus wrote about this concept in this article, if you haven't heard about it.

The concept of hooks actually has huge implications for how we write our components. How? Well, for starters it basically deprecates class components! Hooks now provide the power previously reserved to classes to function components as well!

Here's our sample components as a stateful function component:

import React, { useState } from 'react';

const Gift = props => {
    const [delivered, setDelivered] = useState(false);
    const handleDeliveredClick = () => setDelivered(!delivered);
    return (
        <div>
            <p>{props.name}</p>
            <p>{delivered ? 'Delivered' : 'On its way'}</p>
            {!delivered && <button onClick={handleDeliveredClick}>Deliver</button>}
        </div>
    );
};

As you can see, we get access to state (we "hook" into that feature of React), and we can get access to lifecycles the same way. This pattern is incredibly powerful, and provides React with a lot of the power of the previously deprecated mixin pattern, without any of the downsides.

If you're interested in learning more about hooks, please refer to the extensive documentation. Its coming in a few months - and I honestly can't wait to use it!

So... Death to classes? 😵

So with the arrival of hooks, you'd might think the ES2015 classes are heading for the same fate as React.createClass? Well, yes and no. The React team has hinted that the class component might be moved to a separate package, but nothing is set in stone yet. In addition, there's a few features that hooks doesn't support yet, and a lot of user feedback to consider before we get ahead of ourselves.

So this is it! The definite guide to component creation. We seem to be looking at a future of function components only, but you'll most definitely be seeing both class components and React.createClass in existing codebases and tutorials.