Understanding React Hook at a Glance

Introduction

React hook is a function that lets you dive into state and life cycle features from the function component. In React, hook is the key feature cuz it helps to reduce redundant code and it helps to make components reusable. Before Hooks, you could only use state and other React features inside class components. This meant that if you wanted to write a function component that had a state, you had to convert it to a class component. Hooks are a great way to simplify your components and make them more reusable. They can also help you write more concise and readable code.

Hooks

Here we will look into several hooks that we can often use on a daily basis in our project or we use it in real-time projects. So we will cover the hooks listed below.

  • useState
  • useEffect
  • useReducer

So let's start with the first hook useState.

useState lets you be allowed to add state variables in a component. useState basically updates state value from its initial value, which is given as an argument in useState like useState(initialValue); => here initialValue is assigned means by default value or state will update from the given value.

const [state, setState] = useState(initialValue);

Above, we have defined useState syntax in which state is a state variable that will update on a particular task performed on it, or it will update on an event call.

So when an event call occurs or a task is performed on state, that time state is updated by setState, which is a function that updates the state according to the event triggered. Here we will take a small example of useState.

‚Äčimport React, { useState } from "react";

const Counter = () => {
  const [state, setState] = useState(0);

  return (
    <div className="flex w-full h-full space-y-5">
      <h1>{state}</h1>
      <div className="w-full flex gap-6">
        <button onClick={() => setState(state + 1)} className="m-3 w-36">
          increment
        </button>
        <button onClick={() => setState(state - 1)} className="m-3 w-36">
          decrement
        </button>
      </div>
    </div>
  );
};

export default Counter;

Here, we have provided a small example to understand usestate and how it works. First, we call useState at the top of in component. Then we create a state variable called state and setState, and in the body, we have given two buttons for the increment value and decrement value of the state, we have initialized the initial value as 0 so the count will start from 0.

When we click on the increment button, it will set the state value to +1. This increment is done by the setState function is the second argument in the state variable. If we click on decrement, it will be -1, on the current value of the state so here is the useState use case scenario.

Now, we will look into useEffect.

useEffect is the hook that allows you to synchronize a component with an external system. Now, what is the meaning of the external system? Sometimes, some component needs to synchronize with with external system. For example, you might want to control a non-React component based on the React state, set up a server connection, or send an analytics log when a component appears on the screen. Effects let you run some code after rendering so that you can synchronize your component with some system outside of React.

useEffect(setup, dependencies?)

Above is the definition of use effect, which takes two arguments: the first one is set up and the second dependencies. So what is setup? setup is nothing but the task or calculation that we want to perform at the time of component render or when any event is triggered. It is a logic to perform or update the state. Dependencies are a key feature of the useEffect hook because if we do not add dependencies in useEffect, it will run on every render, so it becomes a very crucial part of handling useEffect according to the need of the component and event trigger.

useEffect(() => {
  // code to run
});
// runs on every render which is not what we want

Above is a declaration without dependencies in use effect, so when we run the project, it will run every time a re-render occurs. This is not what we want at the development time because it leads to a very inconvenient phase where, on every render, it triggers. Without any restriction or without any need.

So, to fix this issue, we can use effect with dependencies like below

useEffect(() => {
}, []);
// runs only on first render

As we can see, this snippet runs only on the first render when a component is mounted. After the component mount, useEffect does not run because we have provided dependencies as an empty array so it will run only the first render. but still, this is not what we want exactly, This means in real-time projects we need to fetch data and update it at some particular time or suppose that we want that use effect to perform tasks only when there are changes in the dependencies array here is the solution given below.

useEffect(() => {
  // Code inside useEffect
}, [prop, state]);
// This runs on first render and also when any value of any dependencies changes, it renders.

Above is the solution for useEffect where we want to trigger it at a time when any one of its dependencies changes. Dependencies are nothing but the array of values or array of stats, and when this stats value changes useEffect also triggers a render and update.

Now, we will look into useReducer.

The useReducet hook is similar to useState, but there is a key difference between them, which we will cover in this. As we know, useState updates the state, and useState passes the state itself; but useReducer does not pass the state itself, but it passes the type of task or logic to perform on the state. useReducer takes two arguments: first is the reducer function, and the second is the initial value.

const [state, dispatch] = useReducer(reducer, initialValue);

So above is the syntax for useReducer, here the state is the state variable on which task will perform dispatch is a function for dispatch type of task to be performed on state, reducer is a function which takes two arguments first is state and second is action, so when event trigger dispatch function is invoked and it directly triggers action method that accepts type of task from dispatch function which going to be performed on the state. In such a way multiple state is managed very conveniently way in a more complex data structure. It makes it easy to handle and manage multiple state variables. An example is given below.

import React, { useReducer } from "react";

const Counter = () => {
  const [count, dispatch] = useReducer(counterReducer, 0);

  function counterReducer(state, action) {
    switch (action.type) {
      case "INCREMENT":
        return state + 1;
      case "DECREMENT":
        return state - 1;
      default:
        throw new Error();
    }
  }

  function handleIncrement() {
    dispatch({ type: "INCREMENT" });
  }

  function handleDecrement() {
    dispatch({ type: "DECREMENT" });
  }

  return (
    <div className="flex w-full h-full space-y-5">
      <h1>{count}</h1>
      <div className="w-full flex gap-6">
        <button onClick={handleIncrement} className="m-3 w-36 bg-slate-400">
          increment
        </button>
        <button onClick={handleDecrement} className="m-3 w-36">
          decrement
        </button>
      </div>
    </div>
  );
};

export default Counter;

The above example is the same as a useState, but we can see the difference between useState and useReducer where useState directly updates the state and passes it, whereas useReducer directly doesn't update the state; instead, it passes the task type to be performed on state.

Conclusion

These hooks are often used in real-time projects. And it makes it more readable and reduces duplication of code. And make components and components logic reusable in the whole project.

Still, there are several hooks that we will cover in the second article.