When I start my React project, my first question is whether to use class components or functional components. Another option is to use both on the project. I hope everyone uses both components on their projects.
React lets you define components as classes or functions.
Before finding the better option, we are going to create class components and functional components.
When creating a React component, the component's name MUST start with an upper case letter.
Class Component
To define a React component class, your class need to extend with React.Component.
The render() method must be defined in the class component. Other React.Component methods are optional like constructor() componentDidMount(), etc.
class Hello extends React.Component {
render() {
return <h1>Hello World!</h1>;
}
}
const element = <Hello />;
ReactDOM.render(
element,
document.getElementById('root')
);
Functional Component
Here we going to change the above class component into a functional component.
function Hello() {
return <h1>Hello World!</h1>;
}
const element = <Hello />;
ReactDOM.render(
element,
document.getElementById('root')
);
Functional components are easy to write with less code and easier to understand. Because it is just a plain JavaScript function that returns JSX.
Differences between functional components and class components
We go through each difference between class components and functional components.
-
- Components Rendering
-
- Handling Props
-
- Handling States
-
- Lifecycle Methods
-
- Accessing Components Children
-
- Higher-Order Components
-
- Error Boundaries
1. Components Rendering
In the class component, the render() method is used for rendering the JSX by extending React.Component
The functional component is just a plain JavaScript function that returns JSX.
The first two programs are a perfect example of component rendering.
// Class component
class Hello extends React.Component {
render() {
return <h1>Hello World!</h1>;
}
}
//Function component
function Hello() {
return <h1>Hello World!</h1>;
}
//Function component with Arrow function
Hello = () => {
return <h1>Hello World!</h1>;
}
Winner: Functional Component
Functional component creation is more simple by using the arrow function. A plain JavaScript function is used as a functional component. No more render method.
Difference between ES6 Arrow functions and Regular functions in JavaScript
2. Handling Props
The props stands for properties and is passed into the React component. It's also used for passing data from one component to another. Read more on the official React documentation.
Class component with Props
this.props used to access our name props.
class Hello extends React.Component {
render() {
return <h1>Hello {this.props.name}!</h1>;
}
}
const element = <Hello name="World"/>;
ReactDOM.render(
element,
document.getElementById('root')
);
Functional component with Props
The valid React functional component should have one argument for the function. The props argument will have all the component props like props.name.
function Hello(props) {
return <h1>Hello {props.name}!</h1>;
}
const element = <Hello name="World" />;
ReactDOM.render(
element,
document.getElementById('root')
);
Winner: Functional Component
No worry about the this keyword. The syntax is clean and simple, it's a winner.
3. Handling State
state is a build-in object of React component, it is used to control the component behaviors. When the state object changes, the component will be re-renders.
Class component with state
class Timenow extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return(
<div>
<h1>Hello {this.props.name}!</h1>
<h2>Time Now {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const element = <Timenow name="World"/>;
ReactDOM.render(
element,
document.getElementById('root')
);
The class component's constructor method is used to initialize the state values.
Functional component with state
In the functional component, the state is handled using the useState Hook.
Hooks are a new addition to React 16.8. They let you use state and other React features without writing a class.
The functional component doesn't have this, so we can't assign or read this.state. Instead, we call the useState Hook directly inside our functional component.
function Timenow(props) {
const [date, setDate] = React.useState(new Date());
return (
<div>
<h1>Hello {props.name}!</h1>
<h2>Time Now {date.toLocaleTimeString()}.</h2>
</div>
);
}
const element = <Timenow name="World" />;
ReactDOM.render(
element,
document.getElementById('root')
);
Winner: Functional Component
The state initialization is done by using the useState hook. No constructor method is required.
4. Lifecycle Methods
In React, each component has several lifecycle methods, this method helps you to run code at particular times in the process. Click here to view the React lifecycle diagram.
Class Component with Lifecycle Methods
The below clock class component is a perfect example for implementing lifecycle methods.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Functional Component with Lifecycle Hook Methods
We converted above the clock class component into a functional component using the useEffect hook.
The useEffect return method is used to clean up.
function Clock(props) {
const [date, setDate] = React.useState(new Date());
React.useEffect(() => {
var timerID = setInterval(() => tick(), 1000);
return function cleanup() {
clearInterval(timerID);
};
});
function tick() {
setDate(new Date());
}
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {date.toLocaleTimeString()}.</h2>
</div>
);
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Winner: Class Component
The functional component useEffect is confusing due to using the same hook for all the lifecycle methods. In class component, we can directly use the methods componentDidMount, componentWillUnmount, etc.
5. Accessing Components Children
The special children prop is used to access the component inside content or component like inside content.
Class Component
this.props.children is used for class components.
class Layout extends React.Component {
render() {
return(
<div>
<h1>Hello {this.props.name}!</h1>
<div>{this.props.children}</div>
</div>
);
}
}
const element = <Layout name="World">This is layout content</Layout>;
ReactDOM.render(
element,
document.getElementById('root')
);
Functional Component
Just props.children is used in functional components to access the children's content.
function Layout(props) {
return(
<div>
<h1>Hello {props.name}!</h1>
<div>{props.children}</div>
</div>
);
}
const element = <Layout name="World">This is layout content</Layout>;
ReactDOM.render(
element,
document.getElementById('root')
);
Winner: Class & Functional Component
Both use the same props.children to access the component children. In addition, we need to use the this keyword.
6. Higher-Order Components
A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOC is a pure function, so it only returns a new component.
Higher-order component is a function that takes a component and returns a new component.
HOC with Class Component
function classHOC(WrappedComponent) {
return class extends React.Component{
render() {
return <WrappedComponent {...this.props}/>;
}
}
}
const Hello = ({ name }) => <h1>Hello {name}!</h1>;
const NewComponent = classHOC(Hello);
const element = <NewComponent name="World" />;
ReactDOM.render(
element,
document.getElementById('root')
);
HOC with Functional Component
function functionalHOC(WrappedComponent) {
return (props) => {
return <WrappedComponent {...props}/>;
}
}
const Hello = ({ name }) => <h1>Hello {name}!</h1>;
const NewComponent = functionalHOC(Hello);
const element = <NewComponent name="World" />;
ReactDOM.render(
element,
document.getElementById('root')
);
Winner: Functional Component
To create HOC, we should use the JavaScript function (classHOC, functionalHOC). Don't Use HOCs inside the render method. Inside the function, we can use a class or functional components.
7. Error Boundaries
Error boundaries are React components used to handle JavaScript errors in React components. So we can catch JavaScript runtime errors in our components and display a fallback UI.
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.
If static getDerivedStateFromError() or componentDidCatch() lifecycle methods used means that the class component becomes an error boundary. Use static getDerivedStateFromError() to render a fallback UI after an error has been thrown. Use componentDidCatch() to log error information.
Error boundaries work as a JavaScript catch {} block, but for components.
Simple example with error boundaries
class ErrorCounter extends React.Component {
constructor(props) {
super(props);
this.state = {error: null, errorInfo: null};
}
componentDidCatch(error, errorInfo) {
// Catch errors in any components below and re-render with error message
this.setState({
error: error,
errorInfo: errorInfo
})
// You can also log error messages to an error reporting service here
}
refreshPage() {
history.go(-1)
}
render() {
if (this.state.errorInfo) {
// Error path
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
<hr />
<button onClick={this.refreshPage}>
Refresh Page
</button>
</div>
);
}
// Normally, just render children
return this.props.children;
}
};
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(({counter}) => ({
counter: counter + 1
}));
}
render() {
if (this.state.counter === 3) {
// Simulate a JS error
throw new Error('I crashed!');
}
return(
<div>
<h1>{this.state.counter}</h1>
<button onClick={this.handleClick}>+</button>
</div>
);
}
}
ReactDOM.render(
<ErrorCounter>
<Counter />
</ErrorCounter>,
document.getElementById('root')
);
Winner: Class Component
Currently, only class components can be Error Boundaries.
Conclusion: Which is better?
Nowadays most apps use functional components due to their simplicity, ease of use, and understanding. Also, functional components have become very famous after hooks were introduced in React v16.8.
Lifecycle methods in functional components are easy to use thanks to hooks.
Class components are hard to understand for new people who don't have a background in ES6 JavaScript where classes were introduced.
Unlike other programming languages such as Java, PHP, and C#, JavaScript classes are syntactic sugar over the prototypal inheritance. In other words, ES6 classes are just special functions. So we cannot use class features in JavaScript classes.
Class components play an important role in Error handling. Because Class Component only supports the Error Boundaries lifecycle methods static getDerivedStateFromError() or componentDidCatch().
Nothing is better, because both have pros and cons. But class components are important to understand React flow and lifecycle methods.
The new learner should practice React using class components. Once they are familiar with class components, they can learn and use functional components.
Thank you for reading! Feel free to comment with your feedback and share the missing differences between class components and functional components.
References
[https://reactjs.org/docs/react-component.html](https://reactjs.org/docs/react-component.html)
[https://reactjs.org/docs/components-and-props.html](https://reactjs.org/docs/components-and-props.html)
[https://reactjs.org/docs/state-and-lifecycle.html](https://reactjs.org/docs/state-and-lifecycle.html)
[https://reactjs.org/docs/higher-order-components.html](https://reactjs.org/docs/higher-order-components.html)
[https://www.twilio.com/blog/react-choose-functional-components](https://www.twilio.com/blog/react-choose-functional-components)
[https://blog.logrocket.com/handling-javascript-errors-react-error-boundaries/](https://blog.logrocket.com/handling-javascript-errors-react-error-boundaries/)
Further Reading
Composable Link Component that Works in Any React Meta-Framework