Class-based React components

Functional React components are great. They get the job done. But there's a type of component that's even better: A class-based component.

Class-based React components are a lot like functional components. You give them props and they render JSX.

But they also have state, lifecycle hooks, and all the bells and whistles that come with a JavaScript class. Let me show you 👇

Take our <Message> component from before

const Message = ({ user, text }) => (
  <div style={{ display: 'flex', height: '200px', padding: '10px 0px' }}>
    <Avatar src={user.avatar} />
    <div style={{ flexDirection: 'column' }}>
      <Name>{user.name}</Name>
      <Text>{text}</Text>
    </div>
  </div>
)

To turn it into a class-based component, we name this function render, and wrap it in a class. Like this:

class Message extends React.Component {
  render() {
    const { user, text } = this.props
    return (
      <div style={{ display: 'flex', height: '200px', padding: '10px 0px' }}>
        <Avatar src={user.avatar} />
        <div style={{ flexDirection: 'column' }}>
          <Name>{user.name}</Name>
          <Text>{text}</Text>
        </div>
      </div>
    )
  }
}

We create a class called Message and define a render method. The method takes user and text out of props, much like before, and returns the same JSX as before.

You'll see this pattern a lot when you work with React. Components often start functional, then turn into class-based components when you need more power.

Notice that props come from this.props instead of the function's arguments. That's where React stores component properties, but don't ever change this object yourself!

Think of props as read only. 🙏🏻

State

If you do need to store something, you should use state.

state is like props that change over time

React uses state to keep track of changes in your components. When you change state, React re-renders your component.

Keep your components pure, relying just on props and state, and you will never render anything you shouldn't. Your UI will be a pure always correct representation of your application logic. More on that in the next chapter.

You can set default component state upon initialization:

// shorthand approach
class Message extends React.Component {
  state = {
    seen: false,
  }
}

// longer approach
class Message extends React.Component {
  constructor() {
    this.state = {
      seen: false,
    }
  }
}

Which approach you can use depends on your toolchain. Most modern React environments support the not-yet-quite-standard public class fields JavaScript feature.

It's safe to use the shorter version. Most codebases I've seen do.

You can access state using this.state:

class Message extends React.Component {
  state = {
    seen: false,
  }

  render() {
    const { user, text } = this.props,
      { seen } = this.state
    return (
      <div style={{ display: 'flex', height: '200px', padding: '10px 0px' }}>
        {seen ? <p>seen before</p> : null}
        <Avatar src={user.avatar} />
        <div style={{ flexDirection: 'column' }}>
          <Name>{user.name}</Name>
          <Text>{text}</Text>
        </div>
      </div>
    )
  }
}

Manipulating state

But don't change values in this.state directly! It's going to cause hard-to-debug issues and stale UI rendering.

Instead you should use this.setState. That way React knows what's going on and can react accordingly.

this.setState({
  seen: true,
})

You can think of setState as running Object.assign on your state object. Existing keys are overwritten with new values, new keys are added.

And it re-renders your component 🤘

Exercise

Try adding a Seent It button to your <Message> component that flips the seen state. Use the onClick event handler.

Lifecycle hooks

Another great feature of class-based components are lifecycle hooks. You can use them to do things based on what React is doing with your component.

We're going to talk about this in more detail later. Right now I just want to show you the basic idea.

Let's say we want to do something when React puts our component onto the page for the first time. Like print out a message saying a <Message> was mounted into the DOM.

class Message extends React.Component {
  componentDidMount() {
    console.log('Message mounted')
  }
}

Add that and when you refresh the page, two messages print out.

Try it 👇

Lifecycle hooks might not seem as useful as component state, and they're not, but they often come in handy. Especially for integrating with third party libraries.

Functional vs. class compnents

There you go. Two types of components. Functional components are light and breezy, and class-based components give you more power with state, lifecycle hooks, and all the other class stuff.

Personally I like to start every component as a function, then expand into a class when necessary.

Know someone who wants to learn React and its whole ecosystem? Share 👇


Cheers,
~Swizec