React Hooks `useState` and `useEffect`

2021.10.11

Introduction

From Version 16.8.0, React witnessed a notable introduction of ‘Hooks’ which allows us to use stateful features along with Functional components. Hooks are functions that allow us to include React state and lifecycle features without using class components, and also helps to organize the logic inside a component into reusable and isolated units.

There are some advantages when we use functional components instead of OOP. Functional-based Programming makes our code a lot easier to read and test as it is plain JavaScript functions without state or lifecycle-hooks and also one can achieve the same functionality with fewer lines of code.

In this blog, let's see more about the useState and useEffect hook.

useState Hook:

In order to use the state of a variable in functional components, React introduced a hook called useState.

Let’s see how to use this hook:

const [count, setCount] = useState(10);

Here useState declares a state variable which is named count. useState function accepts none or one argument which is set as the initial value for the variable. Here I’m setting the initial value of the count as 10.

Variables can be set to any data type. We can also send a function as a parameter to useState hook which in turn returns a value to set your initial value of a variable.

Example:

const [count, setCount] = useState(() => {
 return 10;
});

useState returns an array which includes a variable and a function to update the variable. Here the variable is count and setCount is a function that helps us to update the variable value. Henceforth after updating a value, count always holds the recently updated value.

Example:

setCount(20)
console.log(count) // 20

By executing the above function the value of count is updated from 10 to 20

useEffect Hook

useEffect hook is used to achieve component's life-cycle methods in functional components. useEffect replicates three life-cycle functions in class components. They are componentDidMount(), componentDidUpdate() and componentWillUnmount() functions.

Implementation of componentDidMount() with useEffect() hook:

Generally, componentDidMount() is used to execute a logic only after the first render of the component. We can use useEffect to implement the same by sending two arguments. The first argument should be an anonymous function that gets executed for the first render. The second argument should be an array that tells useEffect when to execute.

To achieve componentDidMount() we use an empty array as the second argument. This empty array indicates execution only at the first render.

useEffect(() => {
   // logic to get executed for the first render of the component
}, []);

Implementation of componentDidUpdate() with useEffect() hook:

To achieve componentDidUpdate() using useEffect, we follow the same method as above. But we need to mention the variables that cause the component to re-render. Let’s say we have n variables in the dependency array. If one of the n values gets updated then the useEffect shall execute the logic that we passed as the first argument to it.

useEffect(() => {
   // logic to get executed
}, [dependency_variable1, dependency_variable2]);

Implementation of componentWillUnmount() with useEffect() hook:

To achieve componentWillUnmount() using useEffect, we use return function. Inside a return function, we generally write logic to clean up or reset our state of variables.

import "./App.css";
import { useState, useEffect } from "react";

function App() {
  const [value, setValue] = useState(true);

  useEffect(() => {
    console.log("ComponentDidMount or componentDidUpdate", value);
    return () => {
      console.log("ComponentWillUnmount", value);
    };
  }, [value]);

  return (
    <div>
      <button onClick={() => setValue(!value)}>Toggle</button>
    </div>
  );
}

export default App;

On first time rendering:

On Second and further renderings it always executes return function first and then renders accordingly:

Implementation of Rendering everytime features with useEffect() hook:

If we want to execute useEffect every time on any update in the component, we can simply avoid sending the array dependency as the second argument to useEffect function(optional argument).

useEffect(() => {
   // logic get executed for the first time and for every update in the component
});

Rules to use hooks in React:

  1. Call Hooks only at the top level. Don’t try to call Hooks inside loops, conditions, or nested functions.
  2. Call Hooks only from React function components. Don’t try to call Hooks from regular JavaScript functions.

Conclusion:

With hooks and functional components together, the code is readable and simple. Rendering is also faster in functional-based as the code size produced by the Babel pre-processor is smaller, this, in turn, reduces overall Webpack chunk size and aids in better performance of the application. Let's get this justified in the below example. Here is a simple comparison of state management in class and functional-based components of a toggle button. The left-hand side has the original code with its size and the right-hand side has the processed code by the Babel:

Class Component Implementation:

Class component

Functional Component Implementation:

functional

The finally produced code by Babel for a class-based component is 699 bytes, whereas for the functional-based component is 356 bytes. Hence the performance is seemed to increase.