Class Components Vs. Functional Components in React

·

4 min read

When I started my React journey back in 2019, hooks had just been introduced a couple of months prior in February of that year. After coming across multiple sources vouching for the ease and simplicity of functional components as opposed to class-based components, I was sold. Right then and there I had decided functional components were the way to go and that I never needed to learn a thing about class components. It wasn't too long before I realized that while I was ready to dive into hooks, not everyone was willing to transition so soon.

Most (at the time, I'd say an easy 95%) of the code snippets I came across on the web (StackOverflow, YouTube tutorials, Code Sandbox, etc.) to aid me in learning React, were written in class components. So, there was my dilemma.

In the end, however, I decided it would probably behoove me to just go ahead and learn both anyway. And so I did.

What are class components?

Before React v16.8, class components were the way React managed state in its applications. State is defined as any piece of data that is capable of changing (via user actions/interactions) causing a re-render of new data to the UI.

In class components, state is handled in the constructor as seen in the snippet below. The constructor() is a method that is created and automatically called when a class object is created. Additionally, because we are extending the React class (React.Component) super() is a function that is always necessary; it calls the constructor of the parent class (React.Component) for our subclasses to work properly. In short: always use the constructor() keyword when creating your own classes. When extending an existing class, always use constructor() and super().

The this keyword always refers to the object that it is currently in. this inside the constructor is referring to the constructor, it will only have access to the items inside the constructor object. this inside the handleNameChange and handleSubmit functions refer to the instance of the App class since arrow functions do not have their own this context, and you can safely use it to access and modify the state using this.setState .

import React from "react";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tempName: "",
      name: "Jane Doe",
    };
  }

  handleNameChange = (e) => {
    this.setState({ tempName: e.target.value });
  };

  handleSubmit = (e) => {
    e.preventDefault();
    this.setState({ name: this.state.tempName, tempName: "" });
  };

  render() {
    return (
      <div className="App">
        <h1>Hello, {this.state.name}!</h1>
        <h2>If you want to change your name, enter it below.</h2>
        <input
          type="text"
          name="name"
          value={this.state.tempName}
          onChange={this.handleNameChange}
        />
        <button type="submit" onClick={this.handleSubmit}>
          Submit
        </button>
      </div>
    );
  }
}

What are functional components?

Initially, functional components were used for simple, stateless presentational components as they were unable to handle state the way class components were able to. With the introduction of React v16.8, hooks were introduced allowing state to be managed inside of functional components.

Hooks are functions that handle state and side effects inside of function components. You know you're working with hooks if a function in React starts with the prefix use.

To convert the class component above to the stateful functional component below, we will use the useState hook. Similar to the constructor, our useState function is what holds application state.

import React, { useState } from "react";

export default function App() {
  const [name, setName] = useState("Jane Doe");
  const [tempName, setTempName] = useState("");

  const handleNameChange = (e) => {
    setTempName(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    setName(tempName);
    setTempName("");
  };

  return (
    <div className="App">
      <h1>Hello, {name}!</h1>
      <h2>If you want to change your name, enter it below.</h2>
      <input
        type="text"
        name="name"
        value={tempName}
        onChange={handleNameChange}
      />
      <button type="submit" onClick={handleSubmit}>
        Submit
      </button>
    </div>
  );
}

In this article, I've used just one hook for simplicity. Check out the first article in the React Hooks series: React Hooks Pt. I - useState.

Numerous instances left me feeling disheartened when I couldn't complete certain projects due to my struggle to comprehend the process of transforming class components into functional counterparts. So, besides the obvious syntax variance, what truly distinguishes these two component types?

In short: not much.

To elaborate further: Functional components have always been an integral part of React, but prior to version 16.8, they lacked the capability to handle state. Consequently, developers had to resort to class components when dealing with state management. However, with the advent of version 16.8, React introduced Hooks, granting functional components the ability to retain state. This pivotal addition eliminated the necessity of exclusively relying on class components.

In summary, both functional and class components yield identical results. The choice between the two ultimately rests upon your personal preference and requirements.