Explore the future of Web Scraping. Request a free invite to ScrapeCon 2024

Which is Better? Class Component Or Functional Component in React?

Differences between functional components and class components in React.

Photo by Possessed Photography on Unsplash

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')
);

Try it on CodePen

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.

Try it on CodePen

Differences between functional components and class components

We go through each difference between class components and functional components.

    1. Components Rendering
    1. Handling Props
    1. Handling States
    1. Lifecycle Methods
    1. Accessing Components Children
    1. Higher-Order Components
    1. 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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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')
);

Try it on CodePen

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




Continue Learning