Functional Components

What are Functional Components?

There are two main types of components in React. Class Components and Functional Components. The difference is pretty obvious. Class components are ES6 classes and Functional Components are functions. The only constraint for a functional component is to accept props as an argument and return valid JSX.

Let’s take a peek:

function Hello(props){
   return <div>Hello {props.name}</div>
}

Boom, that’s a functional component.

The key thing that makes this type of component different from a class component is the lack of state and lifecycle methods. This is why functional components are also commonly referred to as stateless components.

Here’s the same component, but written in ES6:

const Hello = ({name}) => <div>Hello {name}</div>

If you’ve never seen ES6 before don’t panic. It might look a little weird, but it’s not actually that complicated. This article should get you up to speed pretty quickly. Or you can just take my word that these two code snippets do the exact same thing and keep going.

Here’s the same component, but written as a class component:

class Hello extends Component{
   render(){
      return <div>Hello {this.props.name}</div>
   }
}

This snippet is a contradiction to the most basic rule. If you ever have a class component with only a render method – you should always make it a functional component.

So now you know the key differences that make functional components different from class components. Feel free to play around in Code Pen. This example is simple, but you can experiment with adding logic outside of jsx as well as nesting components.

Why use Functional Components?

You might be wondering what all the fuss is about for a type of component that actually removes functionality. But it turns out constraints are often super valuable.

Let’s get into it. Here are the 6 key reasons to start using functional components:

Functional components are easy to reason about

One of the main benefits of functional components is that they make your code easier to read and understand. If you’re working on a project by yourself, you might not think this is a big deal. But trust me. The first time you take a 1-month hiatus from working on a project and come back trying to figure out what your past self was thinking, you’ll understand.

To quote Robert Martin, in his book, Clean Code:

“The ratio of time spent reading versus writing (code) is well over 10 to 1 … making it easy to read makes it easier to write.”

Functional Components are easier to read in large part because you already know all of the things they can’t do, such as have hidden inputs or modify a hidden state. Especially with the use of prop destructuring, it’s very clear what’s going in and coming out of a functional component.

It’s hard to fully understand this benefit until you start more readily using functional components. So I encourage you to dive in. I’ve definitely felt the benefits in my own work.

Functional components are easy to test

It’s easier to test functional components because you don’t have to worry about hidden state or side effects. For every input (props), functional components have exactly one output.

Given certain props, I can assert exactly what the HTML output will be. This means you don’t have to rely on any mocking, state manipulation, or crazy testing libraries. It’s pretty awesome.

Functional components can potentially have a better performance

Since functional components offer no state or lifecycle methods, you would think that this would allow the internals of React to avoid unnecessary overhead such as lifecycle events. Unfortunately, this is currently not the case.

In the words of release notes:

“In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations”

But even without these optimizations, functional components result in less code and faster bundles. This article estimates the current speed up at ~6% with future improvements estimated at ~45%.

Functional components are easy to debug

Functional components depend only on the props they are given to produce an output which in turn makes debugging easier. There is no need to continuously log the state of the component to understand what is going on.

If you know the props being passed in, it’s easy to trace the path of your code and figure out what is taking place.

Functional components are more reusable

This one might be a bit controversial. But by removing function level state, we often make our components easier to use and more widely applicable. Let’s take a peek at two implementations of a custom checkbox.

Functional Checkbox:

const Checkbox = ({ checked, label, handleClick }) => (
   <div
      className={checked ? 'Checkbox-container checked' : 'Checkbox-container'}
      onClick={handleClick}
      role="button"
      tabIndex={0}
      data-label={label}
   >
      <p className="label" data-label={label}>{label</p>
   </div>
);

Making the checkbox a functional component forces us to strip the component down to its most primitive features, which has the side effect of making it more generally applicable. Think of it as a forced best practice.

The checkbox component does not have to choose what its default state should be or what it should do when clicked. Instead, this is delegated to the higher level component. Could you write a class component in the exact same way? Sure, but a functional component forces us to follow best practices which results in a cleaner, more reusable component.

PropTypes

For some reason, some people seem to think that you can’t use PropTypes with functional components. It’s simply not the case. Here’s how I would add PropTypes to the Checkbox component.

Checkbox.propTypes = {
   checked: PropTypes.bool,
   label: PropTypes.string.isRequired,
   handleClick: PropTypes.func.isRequired,
};

Checkbox.defaultProps = {
   checked: true,
};

Class Checkbox:

render(){
   return(
      <div
         className={checked ? 'Checkbox-container checked' : 'Checkbox-container'}
         onClick={handleClick}
         role="button"
         tabIndex={0}
         data-label={label}
      >
         <p className="label" data-label={label}>{label</p>
      </div>
   );
}

First, let’s look at the render method of the class-based component. It looks pretty similar, except that we have the “this” keyword everywhere, and the Checkbox is managing some of its own state. It’s a little more confusing, but overall not too bad.

class Checkbox extends Component{
   constructor(){
      super();
      this.state = {
         checked: false,
      };
      this.handleClick = this.handleClick.bind(this);
   }
   handleClick(){
      this.setState({
         checked: !this.state.checked,
      })
   }

Now let’s look at the rest of the component. All of this code did not exist in the functional component.

If you’re wondering what that weird “.bind” is in the constructor – it has to do with managing the pesky “this” (you can read all about it here). Another thing we don’t have to deal with when we write functional components. Are you sensing a pattern?

Without meaning to, this example also shows many of the other reasons functional components are often preferable to class components. 11 lines of code compared to a total of 30 for the class component. The functional code is clearly easier to understand, and don’t even get me started about trying to test this class component…

To be fair, this class component is poorly written because I wanted to make a point. But the fact that it is so easy to create such a poorly written class component should tell you something.

Despite the scarier code, this component is really doing the same thing, but managing most of its own state. Because of this, we now have to choose some sensible defaults and hope they work in the future. What if we don’t always want the checkbox to toggle state when checked? Or what if we want our checkbox to start in the checked state? We either have to rely on some hackery or write a totally new component. Not ideal.

Functional components can reduce coupling

One of the core concepts used to describe clean code is coupling. Coupling describes the degree of dependency between one entity to another. If our code has low coupling, it means that we can change one area of our code without impacting another. This, in turn, makes our code more maintainable.

Last updated