What is the advantage of using React Hooks over React Classes?

0 Comments

In this article, I would like to talk about the advantages hooks have and how we can use them.

The idea of using hooks makes it possible to create full-fledged functional components, while using all the React features. This allows us to make everything easier, unlike classes.

The main problems which were solved by release of hooks in React 16.8:

  • Wrapper Hell
  • Classes
  • Side effect

If you have worked with React, then you probably know that the most difficult thing is to reuse logic in stateful components.

Before the appearance of React hooks, there was no way to reuse the logic of behavior to the component, for example, to connect it to the store.

Probably  you know such a thing as a HOC (High-Order-Component) or render props. Of course, these are good patterns, unfortunately sometimes they are used excessively and require restructuring of components. This makes the components too large.

This is the wrapper hell.

It's not a secret that an application from some HOC is normal in the current realities, they connected the component to custom HOCs, theme, localization and to the store. It’s getting clear  that the React needed another tool to separate logic.

Using classes - we need to describe lifecycle methods, state of the component, component’s methods that will change our state or work with the store. Also, don’t forget that you need to bind all the methods for the component instance. The component becomes large and it becomes more difficult to read each time.

Classes become unreadable: they grow and often store different logic in one place.

You have to spend additional time optimizing classes: refactoring, decomposing, changing the structure of not only a specific component, but also all associated with it. When a functional component has to use a lifecycle method, you have to rewrite it to a class one.

Using hooks - we can get the state of the component so that it can be easily tested and reused. Now we can facilitate the exchange of links between components or our entire application - using hooks. Hooks allow you to encapsulate logic without affecting the hierarchy of components.

Hooks allow you to do the same by breaking the logic between components into small functions and using them inside the components.

Why do we need classes in React?

Usually for work with local states and lifecycle methods. Hooks allow you to work with them immediately - without writing classes.

Let’s imagine a component that renders a button with text inside and it will be changed by click. We will call handleClick method which change our state according to the previous state value and it will be changed to the opposite.

With using classes:

import React, { Component } from 'react';

class App extends Component {
  constuctor(props) {
    super(props);
    
    this.state = {
      isButtonClicked: false,
    }
    
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState((prevState) => ({
      isButtonClicked: !prevState.isButtonClicked,
    }));
  }

  render() {
    const { isButtonClicked } = this.state;
    
    return (
      <button onClick={this.handleClick}>
        {isButtonClicked ? 'Clicked' : 'Click me, please'}
      </button>
    );
  }
};

With using React Hooks:

import React, { useState } from 'react';

const App = () => {
  const [isButtonClicked, setIsButtonClickedStatus] = useState(false);
  
  return (
    <button
      onClick={() => setIsButtonClickedStatus(!isButtonClicked)}
    >
      {isButtonClicked ? 'Clicked' : 'Click me, please'}
    </button>
  );
};

From this we can conclude that hooks work similarly to such concept like state.

A little more detailed - useState method takes one argument, this is the default value and returns a tuple in which there is the value itself and the method for changing it.

Often in class components, we make side effect functions componentDidMount or componentDidUpdate. We are using componentDidMount for control first rendering and componentDidUpdate for control component’s rendering when the props or state will be changed.

import React, { Component } from 'react';

class App extends Component {
  constuctor(props) {
    super(props);
    
    this.state = {
      isButtonClicked: false,
    }
    
    this.handleClick = this.handleClick.bind(this);
  }
  
  componentDidUpdate() {
    console.log('Button has been clicked');
  }
  
  handleClick() {
    this.setState((prevState) => ({
      isButtonClicked: !prevState.isButtonClicked,
    }));
  }
  
  render() {
    const { isButtonClicked } = this.state;
    
    return (
      <button onClick={this.handleClick}>
        {isButtonClicked ? 'Clicked' : 'Click me, please'}
      </button>
    );
  }
};

In the example above with classes - we are using componentDidUpdate for showing message in console when our state will be updated.

import React, { useState, useEffect } from 'react';

const App = () => {
  const [isButtonClicked, setIsButtonClickedStatus] = useState(false);
    
  useEffect(
    () => {
      console.log('Button has been clicked');
    },
    [isButtonClicked],
  );
    
  return (
    <button
      onClick={() => setIsButtonClickedStatus(!isButtonClicked)}
    >
      {isButtonClicked ? 'Clicked' : 'Click me, please'}
    </button>
  );
};

When we are using hooks - we call useEffect and tell to the react to do a 'side effect' only after updating isButtonClicked state’s value. If other props or state values will be updated - this hook will not be called. It will be triggered only for values which we set in array. Also, you can use empty array for doing side effect only for first rendering or remove second argument  in hook  and it will be called every time when the component will be updated.

Effects are declared inside the component, therefore they have access to props and state. And we can create them in exactly the same way and  as much as you like.

So with hooks - you will be able to control when the side effect functions should be called.

However, the most important thing is - that you can create your own hooks for using them in different components in  your application.

import React, { useState, useCallback } from 'react';

const useCustomHook = () => {
  const [value, setValue] = useState(0);

  const onChange = () => {
    setValue(value + 1);
  }

  return [value, onChange];
}

const App = () => {
  const [isButtonClicked, setIsButtonClickedStatus] = useState(false);
  const [counter, setCounter]                       = useCustomHook();

  const handleClick = useCallback(
    () => {
      setIsButtonClickedStatus(!isButtonClicked);
      setCounter();
    },
    [isButtonClicked]
  );

  return (
    <>
      Clicker counter: {counter}
      <br />
      <button onClick={handleClick}>
        {isButtonClicked ? 'Clicked' : 'Click me, please'}
      </button>
    </>
  );
};

In this example we created custom hook which counts our clicks on a button.

We reuse the state of the component, each call to the useCustomHook function creates an isolated state. It is also important to mention that the beginning of this function must start with the word “use”, which means that it is a hook. I recommend you to follow this format. You can write custom hooks for everything and use it in another components which need this logic.

Hooks are just javascript functions, but they require only two rules:

  • Hooks should be performed at the very top of the function hierarchy (this means that you should not call hooks in conditions and loops, otherwise the reaction cannot guarantee the execution order of hooks)
  • Call hooks only in React functions or functional components or call hooks from custom hooks.

Functional components describe everything as it should be at any given time, and help you not to think about how to respond to changes.

React Hooks allow us to get rid of problems and make it easier to write code.

Comments