14
Class Components vs Functional Components in React
Disclaimer: This article is not about which components are better, but more of a discussion on the differences.
When I started learning React with my bootcamp, we mainly focused on using class components -- if there is initial state, and presentational components if we are just grabbing props. I heard of functional components, but never felt comfortable using it until I started learning Hooks (Remember you can only use Hooks in functional components, not class components).
Let's break down the syntax difference in each of these components!
Difference 1: Rendering JSX
🕹 Syntax 🕹
Class Component (without ES6 destructuring)
import React from 'react';
class App extends React.Component {
render() {
return <h1>Hello, World!</h1>;
}
}
Functional Component
import React from 'react';
function App() {
return <h1>Hello, World!</h1>;
}
🍳 Breakdown 🍳
As you can see above, there are a couple obvious differences in functional component:
- We don't need to extend a component
- We also don't need to use the
render
keyword.
Q: Why do we need to extend the React.Component
class in class component?
A: In React, by extending the React.Component
class, it allows us to pass props to a user defined class/component and inherit methods from React.Component
class, like the lifecycle methods (componentDidMount
, componentDidUpdate
, componentWillUnmount
, render
) and setState
.
📝 Note 📝
In case you don't know, render
is one of the lifecycle methods and the only required method in a class component. It would examine this.props
and this.state
and return types like React elements (JSX), array and fragments, etc. Do not expect it will modify component state!
The React documentation has a very precise and clear explanation on the render
method, as well as the rest of the lifecycle methods. here
⭐️ Additional Note ⭐️
Here's a rule of thumb 👍🏻:
If you only have the render method in your class component, use functional component (which is referred as stateless component sometimes) instead. In functional component, everything defined in the function's body is the render function which returns JSX in the end.
That's how Hooks comes in place as well. In case you want to make a state change in that functional component, you can easily add it without changing to class component by using useState
and useEffect
for lifecycle methods (will cover that in a bit!).
Resources
- Extending React (JavaScript January)
- Why we do extends React.Component when creating the class component in React? (Stack Overflow)
Difference 2: Passing Props
🕹 Syntax 🕹
Let's say we have a props name
from this Component
: <ExampleComponent name="Megan" />
Class Component
class ExampleComponent extends React.Component {
render() {
const { name } = this.props;
return <h1>Hello, { name }!</h1>
// or without destructuring, it will look like this:
// return <h1>Hello, { this.props.name }!</h1>
}
}
Functional Component
// with destructuring
const ExampleComponent = ({ name }) => {
return <h1>Hello, { name }!</h1>
}
// without destructuring
const ExampleComponent = (props) => {
return <h1>Hello, { props.name }!</h1>
}
🍳 Breakdown 🍳
In class component, since it is a class, we have to use this
to refer to the props, or we can destructure it to get name
inside props. Or if we have multiple props, we can do that too:
class ExampleComponent extends React.Component {
render() {
const { name, age, occupation } = this.props;
return (
<div>
<h1>Hello, { name }!</h1>
<p>I am { age } yo and I work as a { occupation }.</p>
</div>
)
}
As for functional components, we are passing props as an argument of the function. Same as above, if we have mutliple props, we can do this:
// with destructuring
const ExampleComponent = ({ name, age, occupation }) => {
return (
<div>
<h1>Hello, { name }!</h1>
<p>I am { age } yo and I work as a { occupation }.</p>
</div>
)
}
// without destructuring
const ExampleComponent = (props) => {
return return (
<div>
<h1>Hello, { props.name }!</h1>
<p>I am { props.age } yo and I work as a { props.occupation }.</p>
</div>
)
}
Difference 3: Handling and Updating state
Before React 16.8 (released in Feb 2019), class component was the only component that can handle state. With the introduction of Hooks and its useState
in React 16.8, we can handle state in functional component! yay!
In case you are not familiar with Hooks and wondering what so special about this Hooks thing, this Intro to Hook from React documentation explains pretty thoroughly.
(Off topic: I personally enjoy reading the React documentation because they are able to explain the most technical concepts in a not so robotic and boring tone, really unlike a lot of the documentations I have read. I highly recommend you to spend some time reading the doc!)
🕹 Syntax 🕹
Class Component
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
name: "Megan"
};
}
// or you can write this without constructor():
// state = {
// count: 0,
// name: "Megan"
// };
render() {
return (
<div>
<h1>Hello, {this.state.name}</h1>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click to add 1
</button>
</div>
)
}
}
Alternatively, you can write the function inside onClick
event before render()
:
class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
name: "Megan"
};
}
// or you can write this without constructor():
// state = {
// count: 0,
// name: "Megan"
// };
handleClick = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<h1>Hello, {this.state.name}</h1>
<button onClick={this.handleClick}>
// or <button onClick={() => this.handleClick()}>
Click to add 1
</button>
</div>
)
}
}
Functional Component
// by the way, I don't want to continue this without explanation
// This is the arrow function, in case you are not familiar
// Alternatively, you can also write
// function ExampleComponent()
// They are basically the same thing.
import React, { useState } from 'react';
// ES6 destructure ^
const ExampleComponent = () => {
const [count, setCount] = useState(0);
// or without destructuring, this will be React.useState(0)
return (
<div>
<h1>Hello, {this.state.name}</h1>
<button onClick={this.handleClick}>
// or <button onClick={() => setCount(count + 1)}>
Click to add 1
</button>
</div>
)
}
🍳 Breakdown 🍳
In class component, we can access the value of the state by using this.state
inside JSX and we would use setState
to update the value of the state. You can set the function inside the event or outside of the render() method -- for readability.
In functional component, we would use useState
to assign initial state and we would use setCount
(in our example) to update the state. If we want to access the value of the state, we can omit this.state
and call the name of the state instead, in our case, it would just be count
.
Q: What's with the square bracket, like [count, setCount]
?
A: The [count, setCount]
syntax is called "array destructuring"!! We are basically making two new variables, in other words,
let countVariable = useState(0);
let count = countVariable[0];
let setCount = countVariable[1];
This can be quite confusing by accessing with 0
and 1
as they have a specific meaning, so React use the "array destructuring" instead.
This is simply the highlight I got from the React documentation, here's the section where you can read in details!
Last but not least...
Difference 4: Lifecycle Methods
useEffect
is the combination of componentDidMount
, componentDidUpdate
and componentWillUnmount
.
componentDidMount
It is invoked immediately after a component is mounted (Mounting means when an instance of a component is being created and inserted into the DOM -- React Doc).
🕹 Syntax 🕹
Class Component
class ExampleComponent extends React.Component {
this.state = {
data: []
}
componentDidMount() {
fetch(someUrlHere)
.then(res => res.json())
.then(data => this.setState(data))
}
render() {
...
}
}
Functional Component
const ExampleComponent = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch(someUrlHere)
.then(res => res.json())
.then(data => setData(data))
}, []);
return (
...
)
}
🍳 Breakdown 🍳
In class component, componentDidMount
is only called once after the first render.
In functional component, we replace componentDidMount
with useEffect
. As we can see there's a []
in the second argument, we usually would put some state we like to update/change, let's say you want to restart a quiz app. useEffect
will only be called if there's any selected changes.
In our case right now, since it is an empty array, useEffect
will be called once on mounting, similar to componentDidMount
.
As you can see in both components, we can set state inside the methods.
Further Reading
- If you are interested in seeing how useEffect works with fetching data using async/await and axios, here's a great article
👩🏻💻Author's Note: I am not so sure how to demonstrate the componentDidUpdate()
and useEffect()
. If you are interested, I am attaching this link from React Doc, this Stack Overflow post and How to mimic componentDidUpdate() with React Hooks from another dev.to writer. Based on my quick research, it looks like we may need useRef()
and custom hook, which currently is out of my knowledge range at the moment.👩🏻💻
componentWillUnmount
It is invoked immediately before a component is unmounted and destroyed. It is usually used for performing any necessary cleanups. One of the most straightforward examples is clear an interval (clearInterval
duh).
🕹 Syntax 🕹
(Code reference from this Stack Overflow post)
Class Component
class ExampleComponent extends React.Component {
this.state = {
data: []
}
// say we have a mounted function that returns a boolean
mounted = () => {
...
}
componentDidMount() {
this.mounted = true;
fetch(someUrlHere)
.then(res => res.json())
.then(data => {
if (this.mounted)) {
this.setState(data)
}
})
}
componentWillUnmount() {
this.mounted = false;
}
render() {
...
}
}
Functional Component
const ExampleComponent = () => {
const [data, setData] = useState([]);
useEffect(() => {
let isMounted = true;
request.get(url)
.then(result => {
if (isMounted) {
setData(result);
}
});
return () => {
isMounted = false;
};
}, []);
return (
...
)
}
🍳 Breakdown 🍳
Not so much of a breakdown, but as you can see:
Cool thing about useEffect
is that you can write functions for both mounting and unmounting in the same place.
componentWillUnmount
is useful when doing cleanups as mentioned above, without that, it can cause severe memory leaks on a bigger project.
Conclusion
As this article is getting longer, I promise I will keep this conclusion section short but short enough to give you room to think about.
React Hooks are taking over in modern React, as it is created to be more relevant and timeless (according to the React doc).
From the comparisons above, we can see how functional components are written shorter and simpler, which makes it easier to read, write and test -- because they are just plain JS functions. However, the rendering time and performance in either components do not make a lot of differences.
I do not necessarily think one is better than the other. A functional programmer may find easier to use functional components, while that applies the same to an object oriented programmer may find easier to use class components.
As I mentioned in the introduction, I started with class components and I am currently in the transition of using functional components, as I like React Hooks a lot and I feel like I can do a lot more with it, but I still feel more comfortable to use the lifecycle methods in class component.
There are a lot of discussions out there which one is better than which and why one prefer over the other. Let me know what you think and let's start a discussion down below!
Further Readings
- Container vs Presentational Components in React
- Functional Components Vs. Class Components In React.Js 👉🏻 They got more in-depth with the analysis and did some performance test
- Understanding Functional Components vs. Class Components in React 👉🏻 Codepen examples
- Introducing Hooks (React Documentation) 👉🏻 As you may have noticed, I have quoted from React documentation so many times in this article. I promise you you will find the documentation super helpful and also since React is a front-end framework, let's all agree that the design of the documentation makes it more fun to read 🙌🏻
- React Class Components vs Functional Components with Hooks: A Never Ending Story by my developer friend, Andrej. As there are discussions around using Hooks or not, Andrej talked about the pros and cons about React Hooks and why it is better with Hooks. Go check it out if you are interested!
If you are looking for more articles/resources to read, I recommend to look for articles that are written after Feb 2019, as it is more relevant to the current React version.
14