React: class components vs function components
Tags: react, javascript, beginners, webdev
Published: 2021-12-01 19:04:36

When I first started working with React, I mostly used function components, especially because I read that class components were old and outdated. But when I started working with React professionally I realised I was wrong. Class components are very much alive and kicking.

So, I decided to write a sort of comparison between class components and function components to have a better understanding of their similarities and differences.

---

## Table Of Contents
- [Class components](#Class-components)
  - [Rendering](#Rendering)
  - [State](#State)
      - [A common pitfall](#pitfall)
  - [Props](#Props)
  - [Lifecycle methods](#Lifecycle)
- [Function components](#Function)
  - [Rendering](#Rendering2)
  - [State](#State2)
  - [Props](#Props2)
- [Conclusion](#Conclusion)

---

## Class components <a name="Class-components"></a>

<img width="100%" style="width:100%" src="https://media.giphy.com/media/3oKIPjHCmuXqdVvak8/giphy.gif">

This is how a class component that makes use of `state`, `props` and `render` looks like:

```jsx
class Hello extends React.Component {

	constructor(props) {
    super(props);
    this.state = {
      name: props.name
    };
  }

  render() {
    return <h1>Hello, {this.state.name}</h1>;
  }
}

// Render

ReactDOM.render(
  Hello,
  document.getElementById('root')
);
```

Related sources in which you can find more information about this:
- [https://reactjs.org/docs/components-and-props.html](https://reactjs.org/docs/components-and-props.html)

### Rendering <a name="Rendering"></a>

Let’s say there is a `<div>` somewhere in your HTML file:

```html
<div id="root"></div>
```

We can render an `element` in the place of the `div` with `root` `id` like this:

```jsx
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
```

Regarding React components, we will usually be exporting a component and using it in another file:

- `Hello.jsx`

```jsx
import React, { Component } from 'react';

class Hello extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

export default Hello;
```

- `main.js`

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './app/Hello.jsx';

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

And this is how a class component gets rendered on the web browser.

Now, there is a difference between rendering and mounting, and Brad Westfall made a great job [summarising it](https://reacttraining.com/blog/mount-vs-render/):

> "Rendering" is any time a function component gets called (or a class-based render method gets called) which returns a set of instructions for creating DOM.
"Mounting" is when React "renders" the component for the first time and actually builds the initial DOM from those instructions.

### State <a name="State"></a>

A state is a JavaScript object containing information about the component's current condition.

To initialise a class component state we need to use a `constructor`:

```jsx
class Hello extends React.Component {

	constructor() {
    this.state = {
      endOfMessage: '!'
    };
  }

  render() {
    return <h1>Hello, {this.props.name} {this.state.endOfMessage}</h1>;
  }
}
```

Related sources about this:
- [https://reactjs.org/docs/rendering-elements.html](https://reactjs.org/docs/rendering-elements.html)
- [https://reactjs.org/docs/state-and-lifecycle.html](https://reactjs.org/docs/state-and-lifecycle.html)

**Caution:** we shouldn't modify the state directly because it will not trigger a re-render of the component:

```jsx
this.state.comment = 'Hello'; // Don't do this
```

Instead, we should use the `setState()` method:

```jsx
this.setState({comment: 'Hello'});
```

If our current state depends from the previous one, and as `setState` is asynchronous, we should take into account the previous state:

```jsx
this.setState(function(prevState, prevProps) {
  return {
    counter: prevState.counter + prevProps.increment
  };
});
```

Related sources about this:
- [https://reactjs.org/docs/state-and-lifecycle.html](https://reactjs.org/docs/state-and-lifecycle.html)

#### A common pitfall <a name="pitfall"></a>

If we need to set a state with **nested objects**, we should spread all the levels of nesting in that object:

```jsx
this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))
```

This can become cumbersome, so the use of the `[immutability-helper](https://github.com/kolodny/immutability-helper)` package is recommended.

Related sources about this:
- [https://stackoverflow.com/questions/43040721/how-to-update-nested-state-properties-in-react](https://stackoverflow.com/questions/43040721/how-to-update-nested-state-properties-in-react)

Before I knew better, I believed that setting a new object property will always preserve the ones that were not set, but that is not true for nested objects (which is kind of logical, because I would be overriding an object with another one). That situation happens when I previously spread the object and then modify one of its properties:

```jsx
> b = {item1: 'a', item2: {subItem1: 'y', subItem2: 'z'}}
//-> { item1: 'a', item2: {subItem1: 'y', subItem2: 'z'}}
> b.item2 = {...b.item2, subItem1: 'modified'}
//-> { subItem1: 'modified', subItem2: 'z' }
> b
//-> { item1: 'a', item2: { subItem1: 'modified', subItem2: 'z' } }
> b.item2 = {subItem1: 'modified'} // Not OK
//-> { subItem1: 'modified' }
> b
//-> { item1: 'a', item2: { subItem1: 'modified' } }
```

But when we have nested objects we need to use multiple nested spreads, which turns the code repetitive. That's where the immutability-helper comes to help.

You can find more information about this [here](https://stackoverflow.com/questions/43040721/how-to-update-nested-state-properties-in-react).

### Props <a name="Props"></a>

If we want to access `props` in the `constructor`, we need to call the parent class constructor by using `super(props)`:

```jsx
class Button extends React.Component {
  constructor(props) {
    super(props);
    console.log(props);
    console.log(this.props);
  }
  // ...
}
```

Related sources about this:
- [https://overreacted.io/why-do-we-write-super-props/](https://overreacted.io/why-do-we-write-super-props/)

Bear in mind that using `props` to set an initial state is an anti-pattern of React. In the past, we could have used the `componentWillReceiveProps` method to do so, but now it's [deprecated](https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops).

```jsx
class Hello extends React.Component {

	constructor(props) {
    super(props);

    this.state = {
      property: this.props.name, // Not recommended, but OK if it's just used as seed data.
    };
  }

  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
```

Using `props` to initialise a `state` is not an anti-patter if we make it clear that the `prop` is only used as seed data for the component's internally-controlled state.

Related sources about this:
- [https://sentry.io/answers/using-props-to-initialize-state/](https://sentry.io/answers/using-props-to-initialize-state/)
- [https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops](https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops)
- [https://medium.com/@justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e](https://medium.com/@justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e)

### Lifecycle methods <a name="Lifecycle"></a>

Class components don't have `hooks`; they have lifecycle methods instead.

- `render()`
- `componentDidMount()`
- `componentDidUpdate()`
- `componentWillUnmount()`
- `shouldComponentUpdate()`
- `static getDerivedStateFromProps()`
- `getSnapshotBeforeUpdate()`

You can learn more about lifecycle methods here:
- [https://programmingwithmosh.com/javascript/react-lifecycle-methods/](https://programmingwithmosh.com/javascript/react-lifecycle-methods/)
- [https://reactjs.org/docs/state-and-lifecycle.html](https://reactjs.org/docs/state-and-lifecycle.html)

---

## Function components <a name="Function"></a>

![Function components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwhub2sxfp6q2g3d71ws.png)
 

This is how a function component makes use of `props`, `state` and `render`:

```jsx
function Welcome(props) {
	const [timeOfDay, setTimeOfDay] = useState('morning');
  
	return <h1>Hello, {props.name}, good {timeOfDay}</h1>;
}

// or

const Welcome = (props) => {
	const [timeOfDay, setTimeOfDay] = useState('morning');
  
	return <h1>Hello, {props.name}, good {timeOfDay}</h1>;
}

// Render

const element = <Welcome name="Sara" />;

ReactDOM.render(
  element,
  document.getElementById('root')
);
```

### Rendering <a name="Rendering2"></a>

Rendering a function component is achieved the same way as with class components:

```jsx
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;

ReactDOM.render(
  element,
  document.getElementById('root')
);
```

Source:
- [https://reactjs.org/docs/components-and-props.html](https://reactjs.org/docs/components-and-props.html)

### State <a name="State2"></a>

When it comes to the state, function components differ quite a bit from class components. We need to define an array that will have two main elements: the value of the state, and the function to update said state. We then need to assign the `useState` hook to that array, initialising the state in the process:

```jsx
import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
```

The `useState` hook is the way function components allow us to use a component's `state` in a similar manner as `this.state` is used in class components.

Remember: function components use **hooks**. According to the official documentation:

> **What is a Hook?** A Hook is a special function that lets you “hook into” React features. For example, `useState` is a Hook that lets you add React state to function components. We’ll learn other Hooks later.
> 

> **When would I use a Hook?** If you write a function component and realize you need to add some state to it, previously you had to convert it to a class. Now you can use a Hook inside the existing function component.
> 

To read the state of the function component we can use the variable we defined when using `useState` in the function declaration (`count` in our example).

```jsx
<p>You clicked {count} times</p>
```

In class components, we had to do something like this:

```jsx
<p>You clicked {this.state.count} times</p>
```

Every time we need to update the state, we should call the function we defined (`setCount` in this case) with the values of the new state.

```jsx
<button onClick={() => setCount(count + 1)}>
  Click me
</button>
```

Meanwhile, in class components we used the `this` keyword followed by the `state` and the property to be updated:

```jsx
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
  Click me
</button>
```

Sources:
- [https://reactjs.org/docs/hooks-state.html](https://reactjs.org/docs/hooks-state.html)

### Props <a name="Props2"></a>

Finally, using `props` in function components is pretty straight forward: we just pass them as the component argument:

```jsx
function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}
```

Source:
- [https://reactjs.org/docs/components-and-props.html](https://reactjs.org/docs/components-and-props.html)

## Conclusion <a name="Conclusion"></a>

Deciding whether to use class components or function components will depend on the situation. As far as I know, professional environments use class components for "main" components, and function components for smaller, particular components. Although this may not be the case depending on your project.

I would love to see examples of the use of class and function components in specific situations, so don't be shy of sharing them in the comments section.

---

🗞️ **NEWSLETTER -** _If you want to hear about my latest articles and interesting software development content, [subscribe to my newsletter](https://chipper-designer-8106.ck.page/1a819bffe8)._

🐦 **TWITTER -** *Follow me on [Twitter](https://twitter.com/DamianDemasi).*