DEV Community

Cover image for React: class components vs function components
Damian Demasi
Damian Demasi

Posted on

React: class components vs function components

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

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

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

Related sources in which you can find more information about this:

Rendering

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

<div id="root"></div>
Enter fullscreen mode Exit fullscreen mode

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

const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

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

  • Hello.jsx
import React, { Component } from 'react';

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

export default Hello;
Enter fullscreen mode Exit fullscreen mode
  • main.js
import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './app/Hello.jsx';

ReactDOM.render(<Hello />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

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:

"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 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:

class Hello extends React.Component {

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

  render() {
    return <h1>Hello, {this.props.name} {this.state.endOfMessage}</h1>;
  }
}
Enter fullscreen mode Exit fullscreen mode

Related sources about this:

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

this.state.comment = 'Hello'; // Don't do this
Enter fullscreen mode Exit fullscreen mode

Instead, we should use the setState() method:

this.setState({comment: 'Hello'});
Enter fullscreen mode Exit fullscreen mode

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

this.setState(function(prevState, prevProps) {
  return {
    counter: prevState.counter + prevProps.increment
  };
});
Enter fullscreen mode Exit fullscreen mode

Related sources about this:

A common pitfall

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

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))
Enter fullscreen mode Exit fullscreen mode

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

Related sources about this:

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:

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

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.

Props

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

class Button extends React.Component {
  constructor(props) {
    super(props);
    console.log(props);
    console.log(this.props);
  }
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Related sources about this:

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.

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

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:

Lifecycle methods

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:


Function components

Function components

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

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

Rendering

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

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

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

ReactDOM.render(
  element,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Source:

State

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:

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

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).

<p>You clicked {count} times</p>
Enter fullscreen mode Exit fullscreen mode

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

<p>You clicked {this.state.count} times</p>
Enter fullscreen mode Exit fullscreen mode

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.

<button onClick={() => setCount(count + 1)}>
  Click me
</button>
Enter fullscreen mode Exit fullscreen mode

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

<button onClick={() => this.setState({ count: this.state.count + 1 })}>
  Click me
</button>
Enter fullscreen mode Exit fullscreen mode

Sources:

Props

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

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Source:

Conclusion

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.

🐦 TWITTER - Follow me on Twitter.

Top comments (33)

Collapse
 
bradwestfall profile image
Brad Westfall

The issue with class based components and the driving reason why the React team went towards functional components was for better abstractions. In 2013 when React came out, there was a feature called mixins (this is before JavaScript classes were possible). Mixins were a way to share code between components but fostered a lot of problems and anti-patterns. In 2015 JS got classes and 2016 React moved towards real class-based components. Everyone was excited that mixins were gone but we also lost a primitive way to share code in React. Without React offering a way to share code, the community turned towards patterns instead.

With classes, if you want to share reusable code between two components, you only really have two pattern choices - higher order components (HoC's) or the "render props" pattern. HoC has several known problems. In other words, I could give you a "try to abstract this" task with classes and you just wouldn't be able to do it with HoC, it had pretty bad limitations. The render props patter was popularized later and it actually fixed all four known issues with HoC's, so a lot of react devs became a fan of this new pattern, but it had new new problems that HoC's never had. I wrote a detailed piece on this a while back gist.github.com/bradwestfall/4fa68...

The reason why hooks were created was to bring functional components up to speed with class based components as far as capability (as you mentioned above) but the end goal of that was custom hooks. With a custom hook we get functional composition capabilities and this solves all six issues of Hoc and Render Props problems, although there are still some good reasons to use render props in certain situations (checkout Formik). If you want, checkout Ryan's keynote at the conference where they announced hooks youtube.com/watch?v=wXLf18DsV-I

Also, the reason why classes are still around is just because the React team knew it would be a while for companies to migrate their big code bases from classes to hooks so they kept both ways around.

Hope it helps someone

Collapse
 
colocodes profile image
Damian Demasi

Wow, thanks so much @bradwestfall ! This is a very interesting back-story on classes and function components. I really appreciate the time you took to explain all of this.

Collapse
 
bradwestfall profile image
Brad Westfall

No problem, your article does a nice job comparing strictly from a syntax standpoint, there's just the whole code abstraction part to consider. Honestly, after teaching hooks now for 3 years, I know that hooks syntax can be harder to grasp than the class syntax, but I also know that most developers are willing to take on the more difficult hooks syntax for the tradeoff of having much better abstraction options, that's really the main idea. For real though, checkout Ryan's conference talk, it's fantastic

Collapse
 
butalin profile image
Anass Boutaline

This is a hot topic bro, nice done, otherwise i guess that functional components are cleaner and easy to maintain, so whatever the size of your app, we always look for better and maintainable code, so FC are better than classes any way (React point of view only)

Collapse
 
tanth1993 profile image
tanth1993

the only thing I like Class Component is that there is a callback in setState. I usually use it when after set loading for the page :)

Collapse
 
gilfewster profile image
Gil Fewster • Edited

The equivalent in functional components is the useEffect hook, which can be setup to run a function when one or more specific dependencies change.

There is also a hook called useReducer which gives you the ability to perform complex actions and logic when dependencies change. Very useful for deriving properties from complex state.

Collapse
 
colocodes profile image
Damian Demasi

Spot on!

Collapse
 
tsolan profile image
Eugene

Some people told, the argument to use class components - error boundaries, which don't have function implementation yet. (It's not my opinion, I just recently started to learn react and seeking for useful information here and there)

Collapse
 
omarpervez profile image
Omar Pervez • Edited

I am new dev in react. I am learning class component. Is that okay for me?

Collapse
 
colocodes profile image
Damian Demasi

When I started learning React, I saw function components first, and then class components. But I think a better approach will be learning class components first, so then, when you learn function components, you will see why they exists and the advantages they have over the class components.

Collapse
 
davido242 profile image
Monday David S.

Totally agree with you

Thread Thread
 
omarpervez profile image
Omar Pervez

We need to learn first Class component and then Functional Component

Collapse
 
omarpervez profile image
Omar Pervez

Yes, I think you are right.

Collapse
 
jeydotc profile image
Jeysson Guevara

You'll need to learn both anyways, it is quite frequent to find projects that mix the two methodologies.

Collapse
 
omarpervez profile image
Omar Pervez

Thank you Jeysson, I think it will help me lot in my react learning

Collapse
 
andrewbaisden profile image
Andrew Baisden

Nice comparison I have completely converted to functional components it would be hard to go back to classes now. When I initially started to learn hooks my thoughts were the reverse.

It really is that much better though.

Collapse
 
colocodes profile image
Damian Demasi

I now have the dilemma of choosing between class or function components at my workplace... I guess that as I gain more experience I will be able to make better decisions.

Collapse
 
colocodes profile image
Damian Demasi

That is awesome @lukeshiru ! Thanks for sharing your experience. I think that what is actually happening is that the app in which I'm working on is rather old, and function components did not exist back then.

Taking into account your experience, do you think that using class components have any benefit over the function components?

Collapse
 
mrmannu profile image
mrmannu

In class components, the render method will be called, whenever the state of the components changes. On the other hand, the Functional components render the UI based on the props. Class Components should be preferred whenever we have the requirement with the state of the component. best regards upflixapk.com/
First of all, the clear difference is the syntax. Just like in their names, a functional component is just a plain JavaScript function that returns JSX. A class component is a JavaScript class that extends React. Component which has a render method.

Collapse
 
standelethan profile image
Ethan Standel

Have you not heard of the React hooks model? React apps haven't needed classes for stateful components since mid 2017.

Collapse
 
standelethan profile image
Ethan Standel

It would bea major mistake to spend time implementing class components into your code. You don't need to remove all your class components, but you should start boyscouting their removal. The React team and the community have both chosen hooks as the defacto pattern system going forward. If you lean against the community, your going to have problems integrating libraries and tools. Not to mention that you're going to run into friction with any new hires, regardless of their experience.

 
colocodes profile image
Damian Demasi

Great, thanks for your input!

Collapse
 
echoes2099 profile image
echoes2099

I was under the impression the official stance was that class components were deprecated...as in dont create new code using these.

We recently had to ditch a form library that was written with classes. The reason being is because it did not have useEffects that reacted to all changes in state (and I'm not sure if you could write the equivalent useEffect with hooks).

So we were seeing bugs where dynamically injected fields could not register themselves.

React hooks are OK but i wouldn't go back to a class based approach for new code

Collapse
 
sophiegrafe profile image
sophiegrafe • Edited

Thank you very much for this, your article and the discussion that follows were a great help to clarify the subject! I will definitely go with FC but take some time to be more comfortable with the class-based approach in case of need.

I have a very little observation to make regarding the way you explained useState affectation "to an array" under "State" in FC section. You wrote: "We need to define an array that will have two main elements[...] We then need to assign the useState hook to that array. [...]"
When I see brackets, as a beginner, it automatically triggers the "array" reflex, but brackets on the left side of the assignment operator means destructuring assignment, here array destructuring.

As I understand this, we don't assign the useState hook to an array, it's the other way around actually, we are unpacking or extracting values from an array and assigning them to variables. useState return an array of 2 values and DA allows us to avoid this kind of extra lines:

const useState = useState(initialValue);
const stateValue = useState[0];
const setStateValue = useState[1];
Enter fullscreen mode Exit fullscreen mode

reactjs.org/docs/hooks-state.html#...
for a more complete review of this syntax:
javascript.info/destructuring-assi...

I found DA very useful in many situations for arrays, strings and objects. Totally worth mentioning, learning and using!
Again thank you!

Collapse
 
caigengliang profile image
chase

Cheers!

Collapse
 
vladi160 profile image
vladi160

Less and clear code for the same result with functions and somehow closer to JS, in my opinion .
Read somewhere that functional components are faster .

Collapse
 
colocodes profile image
Damian Demasi

Interesting take.

Collapse
 
rizzle profile image
Jr Francois

Created an account just to say how much I appreciate this post. Thank you!!

Collapse
 
colocodes profile image
Damian Demasi

Thank you so much @francobb ! I really appreciate it.