Back
React
Sun Jul 21 2024

Understanding React Hooks

React Hooks were first introduced in version 16.8. They are reusable functions in React that allow you to access state and other features without having to create a class. In this post, we will explore the essential hooks in React and how they can simplify your code and enhance your application's functionality.

 

useState()

The `useState` hook in React adds the feature of state management in our functional components. It means that it will enable developers to manage the state of components with ease. We can use this hook multiple times in a single component for different variables.

Syntax

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

The ‘state’ holds the current value of the state variable and ‘setState’ is a function that is used to update the state. When setState is called, it replaces the current state and replaces with the new state passed as an argument.

Implementation

import React, { useState } from "react";
const Counter = () => {
  const [counter, setCounter] = useState(0);
  const increment = () => {
    setCounter(counter + 1);
  };
  return (
    <div>
      <p>{counter}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

export default Counter;

In this example, ‘counter’ variable will first store the value 0. When the ‘Increment’ button is clicked, the increment function will call setCounter function and increase the value by 1.

 

useEffect()

The `useEffect` hook is another essential hook in React. It helps developers in performing side effects on functional components. Side effects are operations that affect something outside the scope of the function being executed such as, data fetching, manual DOM manipulations, and event listener setup.

Syntax

useEffect(()=>{ function ()}, [dependencies]);

The function() in the above syntax means the side effect logic that is going to be implemented when it is called. The ‘dependencies’ is an array that specify the values that the effect depends on. If any of the values in the array change, the effect will rerun.

Implementation

import React, { useEffect, useState } from "react";
import "./App.css";
function App() {
  const [count, setCount] = useState(0);
  const clicked = () => {
    setCount(count + 1);
  };
  useEffect(() => {
    document.title = `You clicked ${count} times.`;
  }, [count]);
  return (
    <div>
      <p>You clicked {count} times.</p>
      <button onClick={clicked}>Click</button>
    </div>
  );
}
export default App;

In the above example, we create a button that increases by 1 everytime we click it which displays the number of times the button has been clicked.

 

useRef()

The `useRef` hook is another hook that is like useState hook. Unlike useState, this hook does not cause our component to update again when the state is changed. It means this hook stores values that we want to keep across renders without causing a re-render when they change. It is used as an easy way to access and manipulate DOM (Document Object Model) elements.

Syntax

const refValue = useRef(initialValue);

The `initialValue` is the value you want to set to the ref object's `current` attribute. useRef returns a ref object called `refValue`.

Implementation

import React, { useRef } from "react";
function Ref() {
  const valueRef = useRef(null);

  const onClick = () => {
    valueRef.current.focus();
  };
  return (
    <div>
      <input type="text" placeholder="Enter your name" ref={valueRef} />
      <button onClick={onClick}>Submit</button>
    </div>
  );
}
export default Ref;

In this example, we use useRef hooks to focus on the input field when the ‘Submit’ button is clicked.

 

useReducer()

The `useReducer` hook is an alternative to useState hook. It is used when we have more complex state transitions. We can use this hook to change two or more events simultaneously. This hook is used when the next state is dependent on the previous state.

Syntax

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

The state holds all the states of our components. ‘Dispatch’ is similar to setState in the useState() hook. It is used to change all the values of our states. The reducer is a function which has two arguments i.e., state and action. The initialization of the states is done in ‘initialArguments’.

Implementation

import React, { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) {
  switch (action.type) {
    case "add":
      return { count: state.count + 1 };
    case "subtract":
      return { count: state.count - 1 };
  }
}
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "add" })}>+</button>
      <button onClick={() => dispatch({ type: "subtract" })}>-</button>
    </div>
  );
}
export default Counter;

The above example uses ‘useReducer’ hook to increase or decrease the count based on the dispatched actions. If we click the ‘+’ button ‘add’ is dispatched and the value increases by 1 whereas the value decreases by 1 if we click the ‘-‘ button.

 

useContext()

The `useContext` is a React hook that helps to share values between components without having to pass props through each component. This hook makes it easier to share data like different themes and user information across various deeply nested components.

Syntax

const value = useContext(Context);

The ‘Context’ in the above syntax passes the props to all the components.

Implementation

//Create a context
import React, { createContext, useState } from "react";

export const UserContext = createContext();

export const UserProvider = ({ children }) => {
  const [userInfo, setUserInfo] = useState({ name: "", email: "" });

  return (
    <UserContext.Provider value={{ userInfo, setUserInfo }}>
      {children}
    </UserContext.Provider>
  );
};
// Provide information to pass
import React, { useContext } from "react";
import { UserContext } from "./UserContext";

function UserInfo() {
  const { userInfo, setUserInfo } = useContext(UserContext);

  const handleUpdateUserInfo = () => {
    setUserInfo({ name: "Sonam Thapa", email: "[email protected]" });
  };
  return (
    <div>
      <h2>User Info</h2>
      <p>Name: {userInfo.name}</p>
      <p>Email: {userInfo.email}</p>
      <button onClick={handleUpdateUserInfo}>Update User Info</button>
    </div>
  );
}
export default UserInfo;

In this example, we update user information directly using useContext. The ‘UserInfo’ component will initially display empty user info. We can update the user information by clicking the "Update User Info" button.

 

useParams()

The `useParams` hook is a React Router DOM hooks that allows us to access the parameters of the current URL to manage dynamic routes in the URL. This hook returns an object when used. It makes it easy to use the parameters used inside the components and display it on the interface.

Syntax

const { parameter } = useParams();

When you call useParams(), it returns an object containing key-value pairs of the route parameters. 

Implementation

//App.js
import React from "react";
import { Link, Route, BrowserRouter as Router, Routes } from "react-router-dom";
import Main from "./Pages/Main";
import About from "./Pages/About";
import UserProfile from "./Pages/UserProfile";
function App() {
  return (
    <Router>
      <Link to="/">Main</Link>
      <Link to="/about">About us</Link>
      <Link to="/userprofile">User Profile</Link>
      <Routes>
        <Route path="/" element={<Main />} />
        <Route path="/about" element={<About />} />
        <Route path="/userprofile" element={<UserProfile />} />
        <Route path="/userprofile/:fname" element={<UserProfile />} />
      </Routes>
    </Router>
  );
}

export default App;

This creates a main page with different links and routes to the respective pages.

//UserProfile.js
import React from "react";
import { useParams } from "react-router-dom";

const UserProfile = () => {
  const { fname } = useParams();
  console.log(fname);

  return (
    <div>
      <h1>Hi! This is {fname} user profile.</h1>
    </div>
  );
};

export default UserProfile;

In the above example, we access the first name of the user and display it on the User profile page.

 

useLocation()

The `useLocation` hook is another React Router DOM hook that returns the current location of our React component. It uses the location object which accesses key, pathname,state, hash and search of the current URL.

Syntax

const location = useLocation();

`location` is the location object returned by useLocation(), which contains information about the current URL. The `location` object contains properties such as pathname, search, hash and state.

Implementation

import React from "react";
import { useLocation, useParams } from "react-router-dom";

const UserProfile = () => {
  const { fname } = useParams();

  const loc = useLocation();

  return (
    <div>
      <h1>Hi! This is {fname} user profile.</h1>
      <h1>Current location: {loc.pathname}</h1>
      <h1>Key: {loc.key}</h1>
    </div>
  );
};

export default UserProfile;

In this example, we access the pathname and the key of the current component that is UserProfile.

  

useMemo()

Memoization is the process of storing a cached value so that we do not have to compute it every single time. The useMemo hook in react uses this memoization to remember the values. It caches the previous results and calculates values only when the dependencies are changed. This React hook must return a value for it to work. The useMemo hook is a performance optimizer.

Syntax

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

The computeExpensiveValue function performs expensive computations and the dependencies are stored in the array.

Implementation

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

function Memo() {
  const [counter, setCounter] = useState(0);
  const [state, setState] = useState(false);

  const increment = () => {
    let i = 0;
    while (i < 2000000000) i++;
    setCounter(counter + 1);
  };

  const memoizedCounter = useMemo(() => {
    let i = 0;
    while (i < 2000000000) i++;
    return counter;
  }, [counter]);

  return (
    <div>
      <div>
        <button onClick={increment}>Counter= {memoizedCounter}</button>
      </div>
      <div>
        <button onClick={() => setState(!state)}>Click me</button>
        <h1>{state ? "Hi!" : "Bye"}</h1>
      </div>
    </div>
  );
}

export default Memo;

In the above example, we explore the use of useMemo in our React applications. `useMemo` ensures that the expensive computation (the loop) is only rerun when counter changes. This improves performance by avoiding unnecessary recalculations on every render.

 

useCallback()

The `useCallback` hook is like useMemo hook in React. The only difference between them is useMemo is for values but the useCallback is used for functions. This hook prevents callback functions from being recreated on each render. It is useful for functions passed as a prop and when passing callbacks to optimized child components.

Syntax

const memoizedCallback = useCallback( () => { // function body },

[dependency1, dependency2, ..., dependencyN]

);

useCallback returns the memoized callback function `memoizedCallback `. The first argument to useCallback is the function that you want to memoize. The second argument is an array of dependents. Only one of these dependencies can cause the memoized function to alter.

Implementation

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

function Memo() {
  const [counter, setCounter] = useState(0);
  const [state, setState] = useState(false);

  const increment = useCallback(() => {
    console.log("Increment button clicked");
    let i = 0;
    while (i < 2000000000) i++;
    setCounter(counter + 1);
  }, [counter]);

  const toggleState = useCallback(() => {
    console.log("Toggle state button clicked");
    setState((prevState) => !prevState);
  }, []);

  return (
    <div>
      <div>
        <button onClick={increment}>Counter= {counter}</button>
      </div>
      <div>
        <button onClick={toggleState}>Click me</button>
        <h1>{state ? "Hi!" : "Bye"}</h1>
      </div>
    </div>
  );
}

export default Memo;

In the above example, `useCallback` is used to memoize the `increment` and `toggleState` functions, ensuring that these functions maintain stable references and are not recreated on every render. This optimization prevents unnecessary re-renders, especially useful when passing these functions as props to child components.

 

useNavigate()

The `useNavigate` hook allows us to redirect or navigate between routes in our application. When we use this hook, we do not need to use ‘Link’ in our application to describe paths. It provides a cleaner and simpler API for navigation compared to older versions of React Router.

Syntax

const navigate = useNavigate();

Implementation

import React from "react";
import { useNavigate } from "react-router-dom";

const Main = () => {
  const navigatetoabout = useNavigate();
  const gotoAbout = () => {
    navigatetoabout("/about");
  };
  return (
    <div>
      Hi! This is the main home page.
      <button onClick={gotoAbout}>Go to About us</button>
    </div>
  );
};
export default Main;

In the above example, we can easily navigate to the About page after clicking the Go to About us button on the Main page.

 

Conclusion

Functional components are more powerful and expressive due to the use of React Hooks. They have changed the way we develop applications. By using different kinds of hooks like useState, useEffect, useRef, useContext, and useReducer, we can manage state, side effects and context sharing in a more simple and efficient way. Furthermore, hooks such as useCallback and useMemo improve performance by memoizing functions and calculations, reducing the number of expensive operations. useNavigate, useLocation, and useParams further enhance functionality by enabling seamless navigation and dynamic routing capabilities in React Router v6 applications. These hooks enable developers to build more responsive and maintainable applications by fully using React's declarative and component-based architecture.