React offers two ways to define components: they can be functional or class-based. If you are new to React, you may be wondering what the difference is between them. The React documentation doesn’t clearly explain when and why you would use one or the other, and what the pros and cons of each type are.
In this article I will help you understand the difference between functional and class-based components. You will know why you may prefer one over another.
What are functional and class-based components?
A functional component is a Javascript function that takes a single parameter or props object and returns a React component. The returned component then is rendered by React.
const MyHello = props => {
return <h2>Hello, {props.name}</h2>;
};
It is even more straightforward if we take advantage of ES6 Arrow functions expressions
const MyHello = props => <h2>Hello, {props.name}</h2>;
The value returned by the function only depends on values passed in props parameter. That is, given the same props, a functional component always renders the same result.
A class-based component is a more complex structure. It is an instance of a class derived from React.Component
class. The class must implement a render()
member function which returns a React component to be rendered, similar to a return value of a functional component. In a class-based component, props are accessible via this.props
.
class MyHello extends React.Component {
render() {
return <h2>Hello, {this.props.name}</h2>;
}
}
Class-based components can have state
Apart from props, the component rendered from a class-based component can depend on its state. Here is an example of a class-based component which maintains a counter in its state. The counter is incremented every time a user clicks on a button which is rendered inside a component.
class MyHello extends React.Component {
// Define an initial state
state = { counter: 0 };
// Call setState to update the component's state
incrementCounter = () => {
this.setState(state => ({
...state,
counter: state.counter + 1
}));
};
render() {
return (
<React.Fragment>
<h2>
Hello, {this.props.name} {this.state.counter} times
</h2>
<button onClick={this.incrementCounter}>Say it again</button>
</React.Fragment>
);
}
}
Class-based components can have lifecycle methods
In addition to a state, class-based components can also have lifecycle methods which fire at specific points of a component lifecycle. Those lifecycle methods can be used to fetch the data from remote servers, create objects, such as timers when the component is mounted (rendered to the DOM for the first time), and free them when a component is unmounted from the DOM, determine if the component should be re-rendered if props are changed, and so on.
// Class-based component with lifecycle methods
class MyHello extends React.Component {
componentDidMount() {
// The component is mounted - fetch the data from the server
...
}
componentDidUpdate(prevProps, prevState, snapshot) {
// The component is updated - update the values
...
}
componentWillUnmount() {
// The component is about to be unmounted - clean up
...
}
render() {
...
}
}
Class-based components can have refs
Class-based components can also have refs to the underlying DOM nodes. Refs may be necessary, for instance, to manage focus in your application for accessibility reasons.
Here is an example of a React component with active focus management via a ref:
// Class-based component with a ref to a DOM element
class MyHello extends React.Component {
constructor(props) {
super(props);
// Create a Ref for the button element
this.inputRef = React.createRef();
}
// Focus on the textInput DOM element
focusOnInput = () => this.inputRef.current.focus();
render() {
return (
<div>
<label htmlFor="textInput">Enter text</label>
<input id="textInput" type="text" ref={this.inputRef} />
{/* Move the focus to the text input
when the button is clicked */}
<button onClick={this.focusOnInput}>
Focus on input control
</button>
</div>
);
}
}
Functional components cannot contain refs because they don’t have instances.
shouldComponentUpdate and PureComponent
shouldComponentUpdate
and PureComponent
are two advanced performance optimisation techniques only available to class-based components. React component re-renders if its props
or state
change. If that happens very often, that may impact your application’s performance.
shouldComponentUpdate
is a special lifecycle method. It allows you to skip the re-rendering even if the props
or state
changed.
class MyHello extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
...
// Return true if the component should re-render
if (...) return true;
// Return false if the component should NOT re-render
else return false;
}
render() {
...
return <div>...</div>;
}
}
You may choose to derive your class-based component from React.PureComponent
rather than React.Component
. In that case React will execute some checks to detect if the state
or props
changes. If your state
or props
are very complex objects, that may save you some precious execution time.
// Class-based component derived from React.PureComponent
class MyHello extends React.PureComponent {
// All you need to do is to extend React.PureComponent
// instead of React.Component.
render() {
...
return <div>...</div>;
}
}
These two techniques have a potential to improve your application performance. However, they should be applied sparingly because they have a potential to break your application if used in a wrong way.
To sum it up, the difference between functional and class-based components is
- Functional component can’t have state or lifecycle methods
- A class-based component can maintain its own state.
- Class-based components can have lifecycle methods which can be used to perform various actions at specific points of the component lifecycle.
- Class-based components can have refs to underlying DOM nodes.
- Class-based components can use
shouldComponentUpdate
andPureComponent
performance optimisation techniques.
Therefore, if your component needs any of these:
- a state (it may render differently for the same set of props)
- lifecycle methods (it needs to perform actions when the component is mounted, unmounted, about to be updated, etc.)
- refs (it needs to refer to underlying DOM nodes)
- Or if you plan to use
shouldComponentUpdate
orPureComponent
performance optimisation techniques.
then you have to use class-based components just because function components don’t have those features.
However, If your components do not require state, lifecycle methods, refs or advanced performance optimisation techniques then neither functional nor class-based components provide clear advantage one over another. In that case, it ultimately comes down to your preference or decision of your team.
Some of the reasons why you may choose functional components over class-based include:
- You are on functional programming path and want to make as much of your code as possible functional,
- You want your code look as clean as possible with a minimal boilerplate,
- Your project enforces
react/prefer-stateless-function
ESLint rule which requires you to write functional components whenever possible.
On the other hand, you may choose class-based components over functional if:
- You find that you often end up converting functional components to classes when you suddenly need state or lifecycle methods during app changes.
- You realise that you lose code clarity if you end up with classes here and functions there. That may be important to big teams where it is crucial to work very clean.