Recently my team was tasked with a project that was developed in ReactJS to be modernized and made more performance optimized. The project was developed using a mix of class components and functional components, without any state management nor any performance considerations for large amounts of data payloads from server. We decided to move the project from class components to functional components, add redux for state management and bring in several performance optimizations. When working with the project I realized that the original development was done without understanding subtle difference and similarities between class components and functional components as well as about the usage of life cycle hooks.
Hence, I thought of sharing some key and fundamental difference between the two so that this could be used as a reference in the future.
- Component Definition
Functional Components
function MyComponent(props) { return <div>Hello, {props.name}!</div>; }
Class Components
import React, { Component } from 'react'; class MyComponent extends Component { render() { return <div>Hello, {this.props.name}!</div>; } }
- State Management
Functional Component with ‘useState
‘
import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Class Component with ‘this.state
‘
import React, { Component } from 'react'; class Counter extends Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
- Lifecycle Methods
Functional Component with ‘useEffect
‘
import React, { useEffect } from 'react'; function Timer() { useEffect(() => { const timer = setInterval(() => { console.log('Tick'); }, 1000); return () => clearInterval(timer); }, []); return <div>Check the console</div>; }
Class Component Lifecycle Methods
import React, { Component } from 'react'; class Timer extends Component { componentDidMount() { this.timer = setInterval(() => { console.log('Tick'); }, 1000); } componentWillUnmount() { clearInterval(this.timer); } render() { return <div>Check the console</div>; } }
- Handling Events
Functional Component
import React from 'react'; function ClickHandler() { const handleClick = () => { console.log('Button clicked'); }; return <button onClick={handleClick}>Click me</button>; }
Class Component
import React, { Component } from 'react'; class ClickHandler extends Component { handleClick = () => { console.log('Button clicked'); }; render() { return <button onClick={this.handleClick}>Click me</button>; } }
- Props and State Comparison
Functional Component
function Greeting(props) { return <h1>Hello, {props.name}</h1>; } function ParentComponent() { const [name, setName] = useState('World'); return ( <div> <Greeting name={name} /> <button onClick={() => setName('React')}>Change Name</button> </div> ); }
Class Component
import React, { Component } from 'react'; class Greeting extends Component { render() { return <h1>Hello, {this.props.name}</h1>; } } class ParentComponent extends Component { constructor(props) { super(props); this.state = { name: 'World' }; } render() { return ( <div> <Greeting name={this.state.name} /> <button onClick={() => this.setState({ name: 'React' })}>Change Name</button> </div> ); } }
- Context API
Functional Component with ‘useContext
‘
import React, { createContext, useContext } from 'react'; const MyContext = createContext(); function ChildComponent() { const value = useContext(MyContext); return <div>{value}</div>; } function ParentComponent() { return ( <MyContext.Provider value="Hello from Context"> <ChildComponent /> </MyContext.Provider> ); }
Class Component with ‘Context.Consumer
‘
import React, { Component, createContext } from 'react'; const MyContext = createContext(); class ChildComponent extends Component { render() { return ( <MyContext.Consumer> {value => <div>{value}</div>} </MyContext.Consumer> ); } } class ParentComponent extends Component { render() { return ( <MyContext.Provider value="Hello from Context"> <ChildComponent /> </MyContext.Provider> ); } }
- Higher-Order Components (HOCs)
Functional Component (HOC)
import React from 'react'; function withLogging(WrappedComponent) { return function(props) { console.log('Component rendered with props:', props); return <WrappedComponent {...props} />; }; } function MyComponent(props) { return <div>{props.message}</div>; } const MyComponentWithLogging = withLogging(MyComponent);
Class Component (HOC)
import React, { Component } from 'react'; function withLogging(WrappedComponent) { return class extends Component { componentDidMount() { console.log('Component rendered with props:', this.props); } render() { return <WrappedComponent {...this.props} />; } }; } class MyComponent extends Component { render() { return <div>{this.props.message}</div>; } } const MyComponentWithLogging = withLogging(MyComponent);
Conclusion
- State Management: Use
useState
in functional components andthis.state
in class components. - Side Effects: Use
useEffect
in functional components and lifecycle methods (componentDidMount
,componentWillUnmount
, etc.) in class components. - Event Handling: Both use similar syntax, but class components use
this
to refer to class methods. - Context API: Use
useContext
in functional components andContext.Consumer
in class components. - HOCs: Higher-Order Components work similarly in both, but syntax differs slightly.
In conclusion, understanding the above key differences and similarities will help to navigate and work between functional and class components effectively.