Functional and class components are two ways to create components in React, but with key differences:
Characteristic | Functional | Class |
---|---|---|
Syntax | Simpler and shorter | More verbose |
State | Hooks (useState) | this.state |
Lifecycle | Hooks (useEffect) | Class methods |
Performance | Better | Worse |
this | Not required | Required |
Modern approach:
In React there are two ways to create components: functional and class components. Although both approaches create components, they have significant differences in syntax, capabilities, and development approaches.
Functional components are regular JavaScript functions that accept props and return JSX:
// Functional component
import { useState } from 'react';
function Welcome({ name }) {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello, {name}!</h1>
<p>Counter: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
</div>
);
}
Class components are ES6 classes that extend React.Component:
// Class component
import { Component } from 'react';
class Welcome extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return (
<div>
<h1>Hello, {this.props.name}!</h1>
<p>Counter: {this.state.count}</p>
<button onClick={() => this.setState({
count: this.state.count + 1
})}>
Increase
</button>
</div>
);
}
}
Functional components are simpler and shorter:
// Functional component
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
Class components are more verbose:
// Class component
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
Functional components use hooks:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Counter: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
</div>
);
}
Class components use this.state:
import { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return (
<div>
<p>Counter: {this.state.count}</p>
<button onClick={() => this.setState({
count: this.state.count + 1
})}>
Increase
</button>
</div>
);
}
}
Functional components use hooks:
import { useEffect, useState } from 'react';
function DataFetcher({ userId }) {
const [data, setData] = useState(null);
// Replaces componentDidMount, componentDidUpdate, componentWillUnmount
useEffect(() => {
fetchData(userId).then(setData);
// Cleanup (componentWillUnmount)
return () => {
// Cancel requests, clear timers
};
}, [userId]); // Dependencies
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}
Class components use lifecycle methods:
import { Component } from 'react';
class DataFetcher extends Component {
constructor(props) {
super(props);
this.state = { data: null };
}
// componentDidMount
componentDidMount() {
this.fetchData(this.props.userId);
}
// componentDidUpdate
componentDidUpdate(prevProps) {
if (prevProps.userId !== this.props.userId) {
this.fetchData(this.props.userId);
}
}
// componentWillUnmount
componentWillUnmount() {
// Cancel requests, clear timers
}
fetchData = async (userId) => {
const data = await fetchData(userId);
this.setState({ data });
}
render() {
return (
<div>
{this.state.data ? JSON.stringify(this.state.data) : 'Loading...'}
</div>
);
}
}
Functional components are usually faster:
// Functional component - less overhead
function FastComponent({ data }) {
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Class components have more overhead:
// Class component - more overhead
class SlowerComponent extends Component {
render() {
return (
<ul>
{this.props.data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
}
✅ Use always when possible:
// Recommended approach
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
setLoading(true);
const userData = await fetch(`/api/users/${userId}`).then(r => r.json());
setUser(userData);
setLoading(false);
};
fetchUser();
}, [userId]);
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
❌ Use only when necessary:
// Only for legacy code
import { Component } from 'react';
class LegacyUserProfile extends Component {
constructor(props) {
super(props);
this.state = { user: null, loading: true };
}
componentDidMount() {
this.fetchUser();
}
componentDidUpdate(prevProps) {
if (prevProps.userId !== this.props.userId) {
this.fetchUser();
}
}
fetchUser = async () => {
this.setState({ loading: true });
const userData = await fetch(`/api/users/${this.props.userId}`).then(r => r.json());
this.setState({ user: userData, loading: false });
}
render() {
if (this.state.loading) return <div>Loading...</div>;
if (!this.state.user) return <div>User not found</div>;
return (
<div>
<h1>{this.state.user.name}</h1>
<p>{this.state.user.email}</p>
</div>
);
}
}
Functional Approach:
import { useState } from 'react';
function Toggle() {
const [isOn, setIsOn] = useState(false);
return (
<button onClick={() => setIsOn(!isOn)}>
{isOn ? 'ON' : 'OFF'}
</button>
);
}
Class Approach:
import { Component } from 'react';
class Toggle extends Component {
constructor(props) {
super(props);
this.state = { isOn: false };
}
render() {
return (
<button onClick={() => this.setState({
isOn: !this.state.isOn
})}>
{this.state.isOn ? 'ON' : 'OFF'}
</button>
);
}
}
Functional Approach:
import { useEffect, useState } from 'react';
function WindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div>
<p>Width: {size.width}px</p>
<p>Height: {size.height}px</p>
</div>
);
}
Class Approach:
import { Component } from 'react';
class WindowSize extends Component {
constructor(props) {
super(props);
this.state = {
width: window.innerWidth,
height: window.innerHeight
};
}
componentDidMount() {
window.addEventListener('resize', this.handleResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
handleResize = () => {
this.setState({
width: window.innerWidth,
height: window.innerHeight
});
}
render() {
return (
<div>
<p>Width: {this.state.width}px</p>
<p>Height: {this.state.height}px</p>
</div>
);
}
}
Functional and class components are two ways to create components in React:
✅ Functional components (modern approach):
❌ Class components (legacy approach):
Key points:
Understanding the difference between these approaches will help you choose the right way to create components and update legacy code when necessary.
Want more articles for interview preparation? Subscribe to EasyAdvice, bookmark the site, and improve every day 💪