Part 09 - React Sidestep 3

Class components, why you need them and when to use them

Last updated by Red Cap Tom on June 24, 2020 at 12:31 +0300

This is no longer maintained

This tutorial series was cowardly abandoned by its creator mid-way due to (exciting!) circumstances. While I can't promise I'll ever finish it, I hope you found some of it useful. If you really-really-really-really want me to finish it, nag me at hey@redcaptom.com.

The Video (Scroll down for the article)

Coming soon!

React class components

Introduction

Generally speaking, you can get along in most simple React applications by just using functional components. If you need to keep some data that changes over the runtime of the application, for example, you can use the useState hook. If you need to do something when a component was added, removed or updated from the application then you can use the useEffect hook.

But, before React 16.8 (which was released in February 2019), hooks were not even a thing. They became available only after that release, meaning that before February 2019 you could not have state in a functional component, or know that it has mounted or unmounted. In practice, you used functional components for the “simple” stuff, and class components for the “complicated” stuff.

This is obviously no longer the case, but I still enjoy thinking in those terms. It offers perspective on what tools are correct for each job, and prevent you from over-complicating things with class components when you can just functionalize (is that a word?) your way to glory.

Class Components Are Heavyweights

Class components are, from my perspective, a more heavyset version of functional components. They offer more fine-grained control over the component and are great as wrappers of functional components, but come at the price of more verbosity, a higher learning curve and less syntactic sugar than functional components. Let's take a look, shall we?

A note

I have to add quick note on what a class actually is in JavaScript land: it's just a wrapper around existing JavaScript ideas, presented in a way that will appeal to Object-Oriented programmers. Put plainly - JS Classes are a way to define what a thing is, and then create occurrences of that thing in your application.

This means that you can define that class once, and then use it (the correct term is “instantiate” it) anywhere else in your application. In practice, we do not do that directly in React - you do not instantiate the class component you create: React does that for you when it mounts the component. You can re-use the component in many places in your application, sure, but you can also do that with functional components, right?

This is confusing, and actually not very relevant if all you're trying to get is a grip on how React looks like so you can pick up its ideas as you go along. For the sake of our discussion, then, I'm going to drop all reference to the JS concept of a class, and focus instead on React class components only. If you're still interested, dive into the docs - they're really good.

How Does One Identify A React Class Component In The Wild?

The General Structure

Excellent question. I'm going to take an example from the official React docs and explain a little bit more on each part then they do there (although, if you come from an Object-Oriented background, you probably already know most of what I'll talk about):

class Clock extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
          date: new Date()
      };
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

This class renders a clock that displays the current hour in HH:MM:SS format to a page. Let's break it piece by piece, starting first with the virtual DOM rendering:

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

We've seen this before, this is how we place components on the DOM. Let's disregard it, since placing components on the virtual DOM is the same in functional and class components. Removing it, then, leaves us with:

class Clock extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
          date: new Date()
      };
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

The Breakdown

The first part of the component is its definition - class Clock extends React.Component . This just means we're using an existing thing from React - the Component class - as the basis for our class. This allows use to create our class by basing it on a set of features React offers in the Component class. Moving on:

  constructor(props) {
      super(props);
      this.state = {
          date: new Date()
      };
  }

A constructor is a special function that is called when the class is instantiated - in our case, when the component is evaluated by React before being placed on the DOM. The props are the attributes we've seen passed to components in the previous example - e.g. in <MainInfo participantName="tom"/> , the MainInfo component's participantName prop has the value tom . The reason we're passing the props to the constructor is so we can have access to them inside the component - otherwise we will not be able to know that participantName had the value tom when the component is mounted, making it difficult for us to display it on the page, for example.

super(props) is another special function that calls the constructor of the upper class our class is extened from - i.e. React.Component . We do that to make sure the concept of props works correctly in our class component, as explained here.

Finally, this.state = {date: new Date()} can be formatted a bit differently for better legibility:

this.state = {
    date: new Date();
}

Which means we're assigning this - i.e. our class Clock - a property called state (yes, like in the useState() hook), that is a JavaScript Object that contains a single field date . We then set this field to contain the current date ( new Date() returns the current date and time). So, what we're basically doing is initializing the state of the object with a single element - the current time.

This is a common pattern in class constructors. We can then change the state by calling a special function called setState() (more about it here - it works like the handler function from useState() we've seen in the previous section), and access any elements in the state of the class by using this.state.X (like in our example - more on this below).

Moving on:

render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }

This part determines what the component does - i.e. how it looks like when rendered. The render() and return() parts are just React's way of saying - this is how the component is going to look like when presented. The interesting part is the JSX (we talked about it previously) between the parantheses of return() :

      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>

This is what the component is going to actually look like - two headers, the first of which is static and the second one referencing the date element in our state . Specifically, we'd like to extract the time in the user's time zone, which is what date.toLocaleTimeString() does.

Phew, that was a bit long, but I hope you got a good sense of what a class component looks like in practice. I'd like to make just a final note on lifecycle methods and nip this in the bud:

The React Component Lifecycle

We are going to add one small thing to our class component - a lifecycle method:

componentDidMount() {
    console.log("Clock has mounted, dude!");
}

So our full component is:

class Clock extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
          date: new Date()
      };
  }

componentDidMount() {
    console.log("Clock has mounted, dude!");
}

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

All this does is log a message in the console when our clock component is being added to the DOM (i.e. when it is mounted). These type of methods allow us to have a finer control of what happens when our component's status changes. Why is this useful, you ask? To me, it's kinda tricky to explain this out of the context of a full application. We will see an example of this being used in the final application I'm building in this series, but for now it's enough to say that it's really convenient to be able to know when a component has entered and left the DOM.

Final words

This is all I have to say about React itself in the context of this tutorial. This is only the tip of the iceberg, as React is a somewhat advanced framework - but should give you at least some intro into what it is you're looking at when reading React code. I do recommend you go buy Dave Ceddia's Pure React - it taught me most of what I use in my apps, and the guy is still updating it today! Hey Dave, if you're reading this, thank you for an awesome book! :)

An offer

If you're working on a Shopify app, and your app uses Polaris for the front-end, I want to hear from you. I am willing to sit down and run a debug session / add a new feature with you for your application, if you agree to stream it live with me (or record it and publish it later). It's not easy writing a full-stack JS app, doubly so when you're not from within the ecosystem. Let's do it together and help all the people! :)

E-Mail me at hey@redcaptom.com, and let's set it up.