Defining components with const vs class in React

Ian Marshall
4 min readMay 25, 2021

While learning React (or any new coding language) I find myself defaulting to following a recipe — I look for another example of a component or function that works, and I use that example to guide my own decision making. I like the analogy of calling this “following the recipe” because it’s very similar to cooking: I’m not going to wake up one day to discover myself transformed into a French chef, but I might learn how to create components or a dish here and there, and one day be able to use those experiences to create something of my own. Without knowing these basics, it’s difficult to believe you’d ever be able to build up to creating a functioning application, though there are often helpful guides along the way.

Following some such guides, I still find myself confused about certain conventions, specifically those surrounding when we as developers should use const or class when defining components in React. Asking my peers and instructors at Flatiron, I found a lot of ambivalence surrounding the topic: some say that using const to create functional components gives a performance boost, but there are other tradeoffs that make these patterns less desirable depending on what you want your component to do. I’m hoping that by explaining some examples of my own, I can offer guidance to anyone who is trying to decide between using const or class to define components in their React App.

Very simply, the biggest difference between using const or class when defining a component is state. is changeable data in a component. Unlike props, state can change during the component’s life. It provides us with a way to update and maintain data within a component that doesn’t rely on a parent component to pass down updated information in the form of props, which can get very complicated depending on how many components your app contains. Class components have state, but components defined with const (also known as Functional components) do not.

Here’s an example of a class component:

import React, { Component } from ‘react’class BigComponent extends Component {   state = {      someValue: “this is a class component.”   }   render() {      return (         <div> I’m the return! {this.state.someValue}</div>      )   }}

Class components allow you to take advantage of lifecycle methods, like componentDidMount(), which allow you to call functions at different points in the component’s lifecycle. This is especially helpful if you want to perform a fetch request, sanitize props when a component updates, or clean up data before a component un-mounts — any function that should be triggered by an aspect of the component’s lifecycle.

Class components are also a JavaScript class, which means you’re creating instances of that class when you deploy class components. This is object-oriented programming: React expects your class component to return a React element from its render function, though this rendering does not add up to much of a difference in time between class components or functional components.

As it happens, class components in React came after functional components, aka components defined with const. Functional components are stateless, which means you can’t employ such helpful tools as referencing this.state, like I did above when accessing “this is a class component” with this.state.someValue. It also means that you can’t use lifecycle methods, though you can use lifecycle hooks, which operate very similarly. Here’s an example with useEffect:

Import React, { useEffect } from ‘react’;const AnotherComponent = ({ someElement }) => {   useEffect(() => {      console.log(“I will log when the value of ‘someElement’ changes”);   }, [someElement]);   return <div>I’m the return! </div>}

In this example, the useEffect hook will evaluate when the value of someElement changes. If we were to write this with class components, it would require a bit more logic to define the same behavior:

import React, { Component } from ‘react’;class BigComponent extends Component {   componentDidUpdate(prevProps) {      if (this.props.someElement !== prevProps.someElement) {         console.log(“I will log when the value of ‘someElement’ changes”);      }   }   render() {      return <div> I’m the return! </div>   }}

There are a lot of other examples of how to use either lifecycle methods in class components or lifecycle hooks in functional components, which accounts for some of the ambivalence surrounding which people prefer to use — if you can accomplish the same functionality in either, why worry about making the wrong choice? One of the biggest differences when writing the code is that class components and lifecycle methods require a bit more imperative programming — for example, if you have any setup like componentDidMount, it mandates you would need componentWillUnmount to clean up. Functional components and their lifecycle hooks have no such necessity since the logic of mounting and un-mounting is included in the function scope.

So, finally, some advice: as a hard and fast rule, since functional components don’t have state (or internal variables), they’re ideal for simple components, like buttons: you can pass any callback function through props like so:

const Button = (props) => (   <button onClick={props.handleClick}> {props.buttonLabel}>   </button>)

In contrast, when any component that relies on Redux, internal react state, or needs data that can’t be passed with a prop, you should use a class component. Another good use is when considering user interaction (does this block need to expand? you’ll probably rely on state to inform that behavior).

--

--