5 Reasons to Use React Hooks Instead of Classes

React hooks have been around for some time now, yet many React developers are not actively using them. I see two primary reasons behind this. The first reason is that many React developers are already involved in a large project, which requires a significant effort to migrate the entire codebase. The other reason is the familiarity with React Classes. With the experience, it feels more comfortable to keep using Classes.

In this article, we’ll look into five reasons why you should consider React Hooks.

1. You don’t have to refactor a functional component into a class component when it grows

With React Hooks, since functional components have the capability of tapping into the state, the refactoring effort will be minimal. Let’s consider the below example, a dumb component that shows a label with a count.

export function ShowCount(props) {
  return (
    <div> 
      <h1> Count : {props.count} </h1>
    </div>
  );
}

Let’s say we need to increment the count with mouse clicks and let’s assume this only affects this particular component. As the first step, we need to introduce the state to our component. Let’s take a look at how we would do that with a class-based approach.

export class ShowCount extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  componentDidMount() {
    this.setState({
      count: this.props.count
    })
  }

  render() {
    return (
      <div> 
        <h1> Count : {this.state.count} </h1>
      </div>
    );
  }
  
}

2. You don’t have to worry about “this” anymore

Classes confuse both people and machines The above sentence is from React documentation. One of the reasons for this confusion is this keyword. If you are familiar with JavaScript, you know that this in JavaScript doesn’t work exactly like in other languages. When it comes to React Hooks, you don’t have to worry about this at all. This is good for beginners as well as experienced developers.

Accessing state using Hooks vs Classes According to the above example, you can see that we no longer have to use “this” while accessing the state. This makes it less confusing for everyone.

3. No more method bindings

Now for the above same ShowCount component let’s introduce a method to update the count of the state when the user clicks on the label.

export class ShowCount extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    this.handleClickEvent = this.handleClickEvent.bind(this);
  }
  componentDidMount() {
    this.setState({
      count: this.props.count
    });
  }

  handleClickEvent() {
    this.setState({count: this.state.count + 1});
  }

  render() {
    return (
      <div>
        <h1 onClick={this.handleClickEvent} > Count : {this.state.count} </h1>
      </div>
    );
  }
}

We have introduced handleClickEvent method. To use it, first, we have to bind it to this of the Component. We have to do this because the execution context is different when the method gets executed. For a beginner developer, this might be a bit hard to understand. Instead of binding all the methods, there are some syntax proposals where you can get around this. For example, we can rewrite the function to an arrow function.

handleClickEvent = () => {
  this.setState({count: this.state.count + 1});
}

Let’s see how we can implement the same functionality with Hooks.

export function ShowCount(props) {
  const [count, setCount] = useState();

  useEffect(() => {
    setCount(props.count);
  }, [props.count]);

  function handleClickEvent() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1 onClick={handleClickEvent}> Count : {count} </h1>
    </div>
  );
}

As you can see, we have only added the function. Also, you might notice that when we use the event handler, we have removed this in the template.

onClick={ handleClickEvent }

4. Easier to decouple logic from UI, making both more reusable

Using hooks, logic and UI are easier to separate. No need for HOC or render props. Hooks do it elegantly with less boilerplate and more intuitive compositions of UI and logic. This “elegant separation” is especially crucial when sharing components using tools and platforms like Bit (Github) as each (independently shared) component is much easier to understand, maintain, and reuse across different apps.

5. Keep related logic in the same place

Complex components become hard to understand With the class-based approach, we have different life cycle methods such as componentDidMountand componentDidUpdate etc. Let's consider a situation where subscribing to services A and B in componentDidMount and unsubscribing them on componentWillUnmount. With time, there will be many logics included in both life cycle methods, and it will be hard to keep track of which part of mounting is related in unmounting. To demonstrate this, let's create an RxJs based service to get the count. We will use this service to update the count in ShowCount example. Note that we will be removing the handleClickEvent as we no longer need to update the component on click events.

import { Subject } from "rxjs";

export function getCounts() {
  const subject = new Subject();
  let count = 0;
  const interval = setInterval(() => {
    if (count > 10 || subject.isStopped) {
      clearInterval(interval);
      subject.complete();
    }
    subject.next(count++);
  }, 1000);

  return subject;
}
 export function ShowCount(props) {
  const [count, setCount] = useState();

  useEffect(() => {
    const countServiceSubject = getCounts();
    countServiceSubject.subscribe((count) => {
      setCount(count);
    });
    return () => {
      countServiceSubject.unsubscribe();
    };
  }, []);

  return (
    <div>
      <h1> Count : {count} </h1>
    </div>
  );
}

ShowCount component with getCounts method in Effect Hook You can see that inside the useEffect we have included subscribing as well as corresponding unsubscribing logic. Similarly, if we need to introduce more service subscriptions or unrelated logics, we can add multiple useEffect blocks to logically separate different concerns.


export function ShowCount(props) {
  const [count, setCount] = useState();
  const [secondCount, setSecondCount] = useState(0);

  useEffect(() => {
    const countServiceSubject = getCounts();
    countServiceSubject.subscribe((count) => {
      setCount(count);
    });
    return () => {
      countServiceSubject.unsubscribe();
    };
  }, []);

  useEffect(() => {
    setSecondCount(secondCount + 1);
  }, []);

  return (
    <div>
      <h1> Count : {count} </h1>
      <h1> Second Count: {secondCount} </h1>
    </div>
  );
}