10 Reasons Why You Should Never Use React Class Components 🙅‍♀️⛔️
Tags: webdev, react, beginners, javascript
Published: 2024-03-18 23:34:24

Whenever I encounter React code with class components, my heart cries 🥲.

Functional components have been the standard for years, yet I still see new React devs using class components.

In this article, I will share 10 reasons why you should avoid class components (plus the only case where you need them).

Ready? Let's dive in 💪 .

## 10 reasons why you should avoid class components like the plague

### Reason #1: They are strongly discouraged by the React team
The React team strongly discourages the use of React class components 😅.
As a result, if you decide to use them:
* You won't get any new features related to class components.
* You won't receive help or get any PDS tickets resolved.   
* You will have a harder time upgrading your version of React since you will need to handle breaking changes.

---

### Reason #2: They are less concise than functional components

A software program should be as concise as possible without losing readability.
There are several reasons for this:
* The more concise the code, the easier it is to read/modify.
* The less code, the fewer bugs in general.
* Code with a lot of boilerplate obscures the actual intention/business logic.
* The fewer code changes you have in your PR, the faster it gets reviewed. If I see a **50-line** change, I will generally review it before a &gt; **300-line change** 🙈.

For all of these reasons, you should prefer functional components. They are more concise than their class equivalent.

See the example below, where the same code is expressed with a class and functional components 👇.

**❌ Bad:** This code is more complex than it needs to be.
```jsx
class Form extends Component {
  state = {
    email: "",
    password: "",
  };
  render() {
    return (
      <>
        <p>
          Email:{" "}
          <input
            type="email"
            value={this.state.email}
            onChange={this.onEmailChange}
          />
        </p>
        <p>
          Password:{" "}
          <input
            type="password"
            value={this.state.password}
            onChange={this.onPasswordChange}
          />
        </p>
      </>
    );
  }
  onEmailChange = (event) => {
    this.setState({ email: event.target.value });
  };
  onPasswordChange = (event) => {
    this.setState({ password: event.target.value });
  };
}
```

**✅ Good:** The code is more concise.
```jsx
function Form() {
  const [{ email, password }, setState] = useState({
    email: "",
    password: "",
  });
  const onEmailChange = (event) => {
    setState((prevState) => ({ ...prevState, email: event.target.value }));
  };
  const onPasswordChange = (event) => {
    setState((prevState) => ({ ...prevState, password: event.target.value }));
  };

  return (
    <>
      <p>
        Email: <input type="email" value={email} onChange={onEmailChange} />
      </p>
      <p>
        Password:{" "}
        <input type="password" value={password} onChange={onPasswordChange} />
      </p>
    </>
  );
}
```

---

### Reason #3: Class components have a ton of lifecycle methods, and many of them will be deprecated in future versions of React

I used to be so confused by React because of all the lifecycle methods: [componentDidMount](https://react.dev/reference/react/Component#componentdidmount), [componentDidUpdate](https://react.dev/reference/react/Component#componentdidupdate), etc.
I was never sure which one to use and was frankly annoyed that I had to write so much code 😒.
Now, with functional components, this is no longer a problem. You still need to be familiar with a component lifecycle, but you don't need those concepts to write a component.
Additionally, many of these methods, like [componentWillUnmount](https://react.dev/reference/react/Component#componentwillunmount), [componentWillUpdate](https://react.dev/reference/react/Component#componentwillupdate), etc., will be deprecated in future React versions. So you can save yourself some migration pain by stopping to use them.

---

### Reason #4: You have to be very careful to bind your class methods to the class instance or declare your methods as arrow functions
This is not a React problem but more something due to how `this` works in Javascript.
In fact, the value of `this` depends on the context in which it appears: function, class, or global. 
The example below shows a **bug** 🐞 in the code because when `onEmailChange` and `onPasswordChange` are invoked, `this` is undefined. As a result, you see an error as soon as you start typing. 

{% embed https://codesandbox.io/embed/cwqttm?view=Editor+%2B+Preview&module=%2Fsrc%2FApp.js %}

To fix this, there are two options:
* **Option #1**: Bind `onEmailChange` and `onPasswordChange` inside the class constructor  
* **Option #2**: Declare `onEmailChange` and `onPasswordChange` as arrow functions

---

### Reason #5: Class components can only read one context at a time
In the latest versions of React, you can only access one context at a time in a class component using [contextType](https://react.dev/reference/react/Component#static-contexttype).
There are workarounds around this, but they make the code more complex.

---

### Reason #6: Using `componentDidMount` often requires using `componentDidUpdate` and `componentWillUnmount`

Unfortunately, as soon as you start using [componentDidMount](https://react.dev/reference/react/Component#componentdidmount), you may need to pair it with [componentDidUpdate](https://react.dev/reference/react/Component#componentdidupdate) and [componentWillUnmount](https://react.dev/reference/react/Component#componentwillunmount).
With functional components, a simple `useEffect` hook is generally enough.

In the sandbox below, you can see that `FunctionalComponentDashboard` is 30 lines while `ClassComponentDashboard` is way longer at 96 lines.

{% embed https://codesandbox.io/embed/lfq85d?view=Editor+%2B+Preview&module=%2Fsrc%2FDasboard.js %}

---

### Reason #7: They prevent you from leveraging powerful tools like hooks
React hooks are amazing.
* They are simple to set up and make "complex" techniques like [higher-order components](https://legacy.reactjs.org/docs/higher-order-components.html) obsolete.   
* You can build your own or leverage external hooks like the ones on [usehooks](https://usehooks.com/)  
* Etc.
Unfortunately, they are not supported inside class components, making code reuse harder.

---

### Reason #8: Most of the documentation and courses use functional components now
It's nearly impossible to find new content about class components.
In fact, since functional components have been the standard for quite some time, they are taught in online courses or used in [react.dev](https://react.dev/) documentation.
So, using class components will put you at a disadvantage and make it harder for new developers to onboard with your codebase.

---

### Reason #9: They can easily break tree-shaking (dead-code elimination)
Tools like Vite, Webpack, etc., optimize your code through **tree-shaking (i.e., dead-code elimination)**.
This is done so that the files sent to users are smaller and they can download them faster.
Now, with class components, it is very easy to mess this up 🥲.
In fact, if you have a method that is not used and you forgot about it, it will also be shipped to the user since tree-shaking won't kick in.

---

### Reason #10: They often result in bigger Javascript files

Class components are more verbose than functional components.
Thus, your app can be slower to start since the user has to download/process bigger files.
Additionally, if you need to support older browsers, they may not support class components 🤦🏾‍♀️.
So you will need a tool like [Babel](https://babeljs.io/) to take those class components and transpile (i.e., transform) them to valid JS code.

This will further increase the total file size (see the comparison below).

**❌ Bad:** With a class component, the resulting JS file is **1.9kb** => [Link](https://babeljs.io/repl#?browsers=last%202%20versions&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=MYGwhgzhAEDCD2BbADvAdgUzQF2gbwChpoAnLAEwxIAoBKQ44s7AVxLWgB4A-ACQxAh40AGYkkolmmDYAlumjAkqTDk4B6bkWgBfAjqA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=true&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-2&prettier=false&targets=&version=7.24.0&externalPlugins=&assumptions=%7B%7D)

![Babel with class component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0aaful3d46qg4m1xdc4m.png)

**✅ Good:** The resulting JS file is way shorter at **224 bytes** => [Link](https://babeljs.io/repl#?browsers=last%202%20versions&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABAYTgWwA4IKZigCgEoBvAKEUQCdsoRKkAeAPgAlsAbduRYS9H8NHhII6LGFxQGAeiakAvkA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=true&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-2&prettier=false&targets=&version=7.24.0&externalPlugins=&assumptions=%7B%7D)

![Babel with functional component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ns63y5pkiuhsotespz7.png)

---

## 💡 The only exception 

Unfortunately, there is no equivalent of the lifecycle method [componentDidCatch](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) for function components. However, this method is required when defining error enhancers. So, this is the only case where you absolutely need a class component.

---

Thank you for reading this post 🙏.

If you were using class components, I really hope you stop using them 🥰.

And Don't forget to Drop a "💖🦄🔥".

If you like articles like this, join my **FREE** newsletter, **[FrontendJoy](https://frontendjoy.substack.com/)**, or find me on [X/Twitter](https://twitter.com/_ndeyefatoudiop).