React: Hooks

React: Hooks

Hooks allow function components to have access to state and other React features. Hooks allow us to "hook" into React features such as state and lifecycle methods.

Types of Hooks:

1) useState() :This hook helps to manage state. You provide an initial value, and it returns two things: the current state and a function to update that state.

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

Example:

import React, {useState} from 'react'
import ReactDOM from 'react-dom'

function App() {
  const [count, setCount] = useState(0)
  const handleIncrement = () => {
    setCount(count + 1)
  }
  const handleDecrement = () => {
    setCount(count - 1)
  }
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('root'))

https://codesandbox.io/p/sandbox/usestateexample-zzt5wg?file=%2Fsrc%2Findex.js%3A2%2C33

2) useContext() :This hook makes it easy to share data between components without having to pass props through every level of the component tree. You create a context using createContext(), and then you can access its value from any functional component using useContext(). This is helpful when you have data that multiple components need access to.

const value = useContext(SomeContext);

Example:

'MyContext.js'

import { createContext } from "react";
const CounterContext = createContext();
export default CounterContext

'CounterComponent.js'

import React, { useState } from "react";
import MyContext from "./MyContext";
import CommentComponent from "./CommentComponent";

const CounterComponent = () => {
  const [count, setCount] = useState(0);
  const handleIncrement = () => {
    setCount((count) => count + 1);
  };

  return (
    <MyContext.Provider value={{ count, handleIncrement }}>
      <CommentComponent />
    </MyContext.Provider>
  );
};

export default CounterComponent;

'CommentComponent.js'

import React from "react";
import MainCounter from "./MainCounter";

const CommentComponent = () => {
  return (
    <>
      <h1>"Hello!!"</h1>
      <MainCounter />
    </>
  );
};

export default CommentComponent;

'MainCounter.js'

import React, { useContext } from "react";
import CounterContext from "./MyContext";

const MainCounter = () => {
  const { count, handleIncrement } = useContext(CounterContext);
  return (
    <>
      <p>Count:{count}</p>
      <button onClick={handleIncrement}>Increment</button>
    </>
  );
};

export default MainCounter;

https://codesandbox.io/p/sandbox/usecontextexample-s8y2dj?file=%2Fsrc%2FCounterComponent.js%3A9%2C5

MyContext.js: Defines a context named CounterContext using createContext() from React.

CounterComponent.js: Contains the main component CounterComponent, which maintains a count state using useState(). It provides the count state and an increment function through the CounterContext.Provider.

CommentComponent.js: Renders a simple component with a greeting message and includes MainCounter component.

MainCounter.js: Consumes the count state and increment function from the CounterContext using useContext(). It displays the current count and a button to increment it.

  • Note: We can also use the library called 'Valtio' . Valtio provides the efficient and simple way to manage state and share state within components.

https://valtio.pmnd.rs/docs/introduction/getting-started

3)useRef(): This hook allows you to reference elements or values that don't need to trigger re-renders. It's commonly used for accessing DOM elements or to store information that we want to keep track of between renders, but we don't necessarily want it to display to the user on screen.

const ref = useRef(initialValue);

useRef() takes only one argument "initialValue", a value you want the ref object’s current property to be initially.

Example:

import React, { useRef, useState } from "react";

function MyForm() {
  const [message, setMessage] = useState("");
  const formRef = useRef();

  const handleSubmit = () => {
    const inputs = formRef.current.getElementsByTagName("input");

    let isFormEmpty = false;
    for (let input of inputs) {
      if (!input.value) {
        input.focus();
        isFormEmpty = true;
        break;
      }
    }
    if (!isFormEmpty) {
      setMessage("Form Submitted successfully");
    }
  };

  return (
    <>
      <form ref={formRef}>
        <input type="text" name="username" placeholder="Username" />
        <input type="password" name="password" placeholder="Password" />
        <button type="button" onClick={handleSubmit}>
          submit
        </button>
      </form>
      {message ? <p>{message}</p> : null}
    </>
  );
}

export default MyForm;

https://codesandbox.io/p/sandbox/userefdemo-96csjs?file=%2Fsrc%2FMyForm.js%3A11%2C32

The useRef hook is to create a reference formRef to the form element. This reference allows the component to directly access the DOM element within the form, particularly the input fields. The reference is then used within the handleSubmit function to retrieve the input elements and check if any of them are empty.

4)useReducer() : The react useReducer() hook is the best alternative for useState hook when you have more complex state building logic or when next state value is depend upon previous state value.

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

useReducer() takes three arguments

  • reducer: it is a function that specifies how state gets updated, reducer function has two arguments state and action, it will return next state.
  • initialArg: The value from which the initial state is calculated.
  • init (optional) : The initializer function that should return initial state.

useReducer() returns an array with two values

  • state: The current state. During the first render it is set to init(initialArg) or initialArg when there is no init.
  • dispatch: A function that lets you update the state to a different value and trigger a re-render, You need to pass the action as the only argument to the dispatch function.

Example:

import React, { useReducer } from "react";

const reducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    case "reset":
      return { count: 0 };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>Increment</button>
      <button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
      <button onClick={() => dispatch({ type: "reset" })}>Reset</button>
    </div>
  );
};

export default Counter;

The useReducer hook to manage counter state. The reducer function updates the count based on actions like "increment", "decrement", or "reset". The Counter component renders the current count and three buttons to perform these actions.

https://codesandbox.io/p/sandbox/usereducerexample-ykws39?file=%2Fsrc%2Fcounter.js%3A30%2C1

5)useMemo() : The react useMemo is designed to optimize performance by memoizing the results of expensive computations. By caching the computed value and only recalculating it when its dependencies change. This makes it ideal to improve the efficiency of your React applications .

const cachedValue = useMemo(calculateValue, dependencies)

Example:

import React, { useState, useMemo } from "react";

const FactorialCalculator = ({ number }) => {
  const factorial = useMemo(() => {
    let result = 1;
    for (let i = 1; i <= number; i++) {
      result *= i;
    }
    return result;
  }, [number]);

  return (
    <div>
      <p>Number: {number}</p>
      <p>Factorial: {factorial}</p>
    </div>
  );
};

const Factorial = () => {
  const [number, setNumber] = useState(0);

  return (
    <div>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(parseInt(e.target.value))}
      />
      <FactorialCalculator number={number} />
    </div>
  );
};

export default Factorial;

https://codesandbox.io/p/sandbox/usememoexample-ggxsw7?file=%2Fsrc%2FFactorialCalculator.js%3A11%2C1

The useMemo hook, is used to optimize the calculation of the factorial of a number. First argument to useMemo hook is the function for calculating factorial, and second argument is the array of dependency, which is 'number', So, whenever the number changes, the factorial calculation will be recomputed. By this, we are avoiding unnecessary recalculations.

Read more