The difference between Classes and Function components in React
Franco D'Alessio
April 21, 2020 • 5 min. readIn the early days of React, Classes were the only way to have functionality in your components (like state
). You’d only use Functions for dumb components which only displayed stuff.
This is no longer true, because we now have React Hooks and they allow us to have in Function components the same functionality we used to have with Classes.
However, there is one key difference, that not everyone is aware of 🔮
Let’s start with a simple example
The best way to understand this concept is by seeing it in action.
Let’s create a simple app where you can select a Simpsons’ character from a list. Then when you press a button, an alert will show a greeting to that character after 4 seconds.
We will create the SayHi
component as a Class and also as a Function, to see the difference.
Our two SayHi components
First, let’s write our Function component:
import React from "react";
const SayHiFunction = ({ name }) => {
const handleClick = () => {
setTimeout(showAlert, 4000);
};
const showAlert = () => {
alert(`Hello ${name}!`);
};
return (
<div>
<button onClick={handleClick}>
Say Hi with Function
</button>
</div>
);
};
export default SayHiFunction;
As you can see, it’s a very simple component. It receives name as a prop, and then when you click the button, a timeout is set to show the alert after 4 seconds.
Now let’s transform this Function into a Class:
import React, { Component } from "react";
class SayHiClass extends Component {
handleClick = () => {
setTimeout(this.showAlert, 4000);
};
showAlert = () => {
alert(`Hello ${this.props.name}!`);
};
render() {
return (
<div>
<button onClick={this.handleClick}>
Say Hi with Class
</button>
</div>
);
}
}
export default SayHiClass;
Pretty easy, right? I’m sure almost anyone with some React knowledge would be able to do this transformation.
You can play with the app in this Codesandbox.
However, this two components will not behave the same 😕
So what’s the difference?! 😠
Ok, the important part. Let’s do this:
- Select “Marge” from the list
- Press the button
- Change the character to “Homer” before 4 seconds pass
What do you expect to happen? Do you expect the alert to say “Hello Marge” or “Hello Homer”?
Again, right now the important thing is not what you think will happen, but what would you expect to happen in this case.
Of course, we would expect to see “Hello Marge”. That was the selected character when we pressed the button.
Now that we’re on the same page on that, what do you think will happen?
Let’s take a look at the Function component first:
Good! It behaves as we expected and the alert says “Hello Marge”.
Let’s try the Class component now:
Mmm, that was not what we expected. We changed the selected character after pressing the button, and the alert message also changed 🤔
What the f*%! happened?
Well, our two components are not the same. Of course, one is a Class and the other is a Function, but we also made a mistake when we transformed the Function into a Class.
The example seemed obvious and straightforward, and that’s the transformation most people would do. It’s common to think those two pieces of code are equivalent. However, it’s wrong because of the main difference between Classes and Function components:
Function components capture the rendered values.
What does that mean?
Let’s analyze our example to understand this.
When we used the Class component, the alert message changed after we changed the selected character.
That happens because our method is reading from this.props.name
.
But props are immutable in React, that’s not the issue.
It’s true, props are immutable, but… this
is mutable. And it makes sense, because React mutates this
over time so you can read the fresh version in render
and lifecycle methods.
So in our example, by changing the selected character the second time, we re-render the component, so this.props
is updated. The method is reading from the latest, newest props.
That’s a problem because we weren’t expecting that. We would expect our event handlers to be “linked” to a particular render, with particular props.
By setting a timeout whose callback reads this.props
, we’re making that impossible. Our callback will not be “linked” to a particular render, so it doesn’t know what the “correct” props are.
We don’t have this issue with the Function component because, once again:
Function components capture the rendered values.
That means that even if we change the character a second time, the method will display the alert using the selected character when the button was pressed, because that value was captured. Unlike this
, the props
are immutable so the object is never mutated by React.
When the parent component renders SayHiFunction
with different props, React will call the SayHiFunction
again. But the event handler we already clicked is “linked” to the previous render with its own name
value and the showAlert
callback that reads it.
So Classes are useless?
No! Not at all. Classes are fine, we’re having a bug here because our implementation is incorrect, different from the original function component.
If you don’t want to use a Function component, there are many ways to fix this issue. For example, you could do this:
import React, { Component } from "react";
class SayHiClass extends Component {
handleClick = () => {
const { name } = this.props;
setTimeout(() => this.showAlert(name), 4000);
};
showAlert = (name) => {
alert(`Hello ${name}!`);
};
render() {
return (
<div>
<button onClick={this.handleClick}>
Say Hi with Class
</button>
</div>
);
}
}
export default SayHiClass;
Our props were getting “lost” before, so in this case we are reading and capturing them early during the event.
Again, this is just one way of fixing it, there are many others but that’s not the focus of this post.
That’s it!
I hope this post was useful and now you understand the main key difference between Classes and Function components.
Thanks for reading ❤️