DEV Community

Megan Lo
Megan Lo

Posted on • Edited on

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>;
  }
}
Enter fullscreen mode Exit fullscreen mode

Functional Component

import React from 'react';

function App() {
  return <h1>Hello, World!</h1>;
}
Enter fullscreen mode Exit fullscreen mode

🍳 Breakdown 🍳

As you can see above, there are a couple obvious differences in functional component:

  1. We don't need to extend a component
  2. 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


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> 
  }
}
Enter fullscreen mode Exit fullscreen mode

Functional Component

// with destructuring
const ExampleComponent = ({ name }) => {
  return <h1>Hello, { name }!</h1>
}

// without destructuring
const ExampleComponent = (props) => {
  return <h1>Hello, { props.name }!</h1>
}
Enter fullscreen mode Exit fullscreen mode

🍳 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> 
    )
}
Enter fullscreen mode Exit fullscreen mode

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> 
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

🍳 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];
Enter fullscreen mode Exit fullscreen mode

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() {
   ...
  }
}
Enter fullscreen mode Exit fullscreen mode

Functional Component

const ExampleComponent = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch(someUrlHere)
      .then(res => res.json())
      .then(data => setData(data))
  }, []);

  return (
    ...
  )
}
Enter fullscreen mode Exit fullscreen mode

🍳 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() {
   ...
  }
}
Enter fullscreen mode Exit fullscreen mode

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 (
    ...
  )
}
Enter fullscreen mode Exit fullscreen mode

🍳 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

  1. Container vs Presentational Components in React
  2. Functional Components Vs. Class Components In React.Js 👉🏻 They got more in-depth with the analysis and did some performance test
  3. Understanding Functional Components vs. Class Components in React 👉🏻 Codepen examples
  4. 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 🙌🏻
  5. 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.

Top comments (9)

Collapse
 
branislavtotic profile image
Branislav Totic • Edited

So basic rules for useEffect:

  1. on Mount: useEffect(() => { // some logic }, []);
  2. on Every state/props change: useEffect(() => { // some logic });
  3. on Every conditional param change: useEffect(() => { // some logic }),[ param1, param2 ];
  4. on unMount: useEffect(() => { // some logic return () => { // some umount logic } });
Collapse
 
mehmehmehlol profile image
Megan Lo

thank you for commenting this!!! this is super helpful!

Collapse
 
alekseiberezkin profile image
Aleksei Berezkin

Functional components are much much easier than class ones. Once I rewrote an app to functional components and never regret of this. Yet, writing custom hooks is much easier than writing higher order components.

Collapse
 
mehmehmehlol profile image
Megan Lo

I am still learning about custom hooks atm! Would you mind elaborating more on that and how it is easier than writing HOC for readers who just started exposing to functional components? thanks!

Collapse
 
alekseiberezkin profile image
Aleksei Berezkin

I'm not sure I can explain this in couple of words 🙂 In general, HOCs require you to combine and destructure passed props which is always cumbersome. Implementing some logic atop of it adds more complexity. Perhaps you find interesting my post on creating no-lib global store. I implement it with hooks, and that's trivial; then reimplement the same with HOCs, and that looks very convoluted.

Collapse
 
alizulfaqar profile image
Ali Zulfaqar

Thank you for creating this post, i am also trying to learn how to code with functional component.

Collapse
 
aspraveen profile image
praveen

Nice explanation!

Collapse
 
emurrell_76 profile image
EMurrell

Nice breakdown and analysis. Personally I'm a much bigger fan of Hooks/ functional components. It's interesting to see them compared.

Collapse
 
mehmehmehlol profile image
Megan Lo

After using Hooks on one of my projects and spending a lot more time on Hooks documentation, I am convinced to use Hooks more often and definitely see a lot more benefits from that 🙌🏻