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 > 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.
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.
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, 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, 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.
To fix this, there are two options:
-
Option #1: Bind
onEmailChange
andonPasswordChange
inside the class constructor -
Option #2: Declare
onEmailChange
andonPasswordChange
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.
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, you may need to pair it with componentDidUpdate and 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.
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 obsolete.
- You can build your own or leverage external hooks like the ones on usehooks
- 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 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 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
β Good: The resulting JS file is way shorter at 224 bytes => Link
π‘ The only exception
Unfortunately, there is no equivalent of the lifecycle method componentDidCatch 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, or find me on X/Twitter.
Top comments (0)