Class vs. Functional Components in React

Hero image for Class vs. Functional Components in React. Image by Elimende Inagella.
Hero image for 'Class vs. Functional Components in React.' Image by Elimende Inagella.

In React, we have two primary ways to create our components: class components and functional components. Whilst class components were the standard for many years, functional components have become increasingly popular, especially with the introduction of React Hooks.

In this article, I will explore the differences between class and functional components, their advantages and drawbacks, and when to use each approach. Hopefully, by the end, you will understand how to decide between them in different scenarios.


What are Class Components?

Class components are ES6 classes that extend React.Component. They allow you to manage state and lifecycle methods within a single component.

An Example of a Class Component:

import React, { Component } from "react";class Counter extends Component<{ initialCount: number }, { count: number }> {  constructor(props: { initialCount: number }) {    super(props);    this.state = { count: props.initialCount };  }  increment = () => {    this.setState((prevState) => ({ count: prevState.count + 1 }));  };  render() {    return (      <div>        <p>Count: {this.state.count}</p>        <button onClick={this.increment}>Increment</button>      </div>    );  }}export default Counter;

Here we have a fairly basic example of a class component that manages its own state using this.state and updates it with this.setState(). It renders a button which increments the count when clicked.

Key Characteristics of Class Components

  • Must extend React.Component or React.PureComponent.
  • Use this.state to manage local state.
  • Require lifecycle methods like componentDidMount and componentDidUpdate.
  • Require binding for event handlers unless using arrow functions.

What are Functional Components?

Functional components are simpler and are defined as functions that return JSX. Before React Hooks, they were limited to stateless components, but Hooks have enabled them to handle state and side effects in much the same way (albeit simpler) than a class component can.

Example of a Functional Component

import React, { useState } from "react";const Counter: React.FC<{ initialCount: number }> = ({ initialCount }) => {  const [count, setCount] = useState(initialCount);  return (    <div>      <p>Count: {count}</p>      <button onClick={() => setCount(count + 1)}>Increment</button>    </div>  );};export default Counter;

This is basically the same component as we discussed above, except that it is a functional component, using the useState Hook to manage its own state. It also renders a button that increments the count when clicked.

Key Characteristics of Functional Components

  • Defined as functions that return JSX.
  • Uses useState, useEffect, and other Hooks for state and lifecycle behaviour.
  • Does not require this keyword.
  • More concise and easier to read.

Comparing Class and Functional Components

FeatureClass ComponentsFunctional Components
State Managementthis.state and setStateuseState Hook
Lifecycle MethodscomponentDidMount, etc.useEffect Hook
PerformanceSlightly heavierLighter and faster
Code ReadabilityMore boilerplateMore concise
Best for Complex LogicYesNow possible with hooks

Error Boundaries in React

Error boundaries are special components that catch JavaScript errors anywhere in their child component tree and display a fallback UI instead of crashing the entire application. They are particularly useful for handling unexpected runtime errors in production.

An Example of an Error Boundary Component

To offer a more concrete example:

import React, { Component, ErrorInfo } from "react";class ErrorBoundary extends Component<{ children: React.ReactNode }, { hasError: boolean }> {  constructor(props: { children: React.ReactNode }) {    super(props);    this.state = { hasError: false };  }  static getDerivedStateFromError(): { hasError: boolean } {    return { hasError: true };  }  componentDidCatch(error: Error, errorInfo: ErrorInfo) {    console.error("Caught error:", error, errorInfo);  }  render() {    if (this.state.hasError) {      return <h2>Something went wrong.</h2>;    }    return this.props.children;  }}export default ErrorBoundary;

Here, ErrorBoundary catches errors from its child components and displays a fallback message instead of allowing the app to crash. As of now, only class components can be used as error boundaries, which is one of the (very) few remaining reasons to use class components in React rather than their functional counterparts.


When to Use Class vs. Functional Components

Use Class Components When:

  • You're maintaining an older React codebase that already uses classes.
  • You need error boundaries (currently, only class components can be error boundaries).

Use Functional Components When:

  • You want simpler, more readable code.
  • You need improved performance due to fewer rerenders.
  • You're using modern React best practices with Hooks.
  • You want to ensure easier future maintenance, as React's development focus is now on functional components.

Should You Use Class Components in New Projects?

It is fair to say that functional components should be the default choice for the vast majority of new projects. They offer a more concise syntax, better performance, and greater flexibility with Hooks. The only remaining reason to use class components is for error boundaries, but even that may change in future React releases.


Wrapping up

With the introduction of Hooks, functional components now offer nearly all the capabilities of class components whilst being simpler, more concise and performant. As I've already said just above: for any modern React development or new projects, you should be using functional components as the default choice. Class components still serve a purpose in legacy codebases and for implementing error boundaries, but they are no longer the preferred approach for new projects.

Key Takeaways

  • Class components use this.state and lifecycle methods, making them more verbose.
  • Functional components with Hooks can manage state and side effects in a simpler way.
  • Functional components are now the preferred approach for most new React projects.
  • Understanding both helps when working with legacy React applications.

By understanding these differences, you can make informed decisions when building React applications.


Categories:

  1. Development
  2. Front‑End Development
  3. Guides
  4. JavaScript
  5. React