1. Home
ReactJS

ReactJS Tutorial: A Comprehensive Guide

Master ReactJS with our in-depth tutorials. From beginner to advanced topics, explore essential concepts and build dynamic web applications effortlessly.

  • 10
  • 3
right-top-arrow
7

Mastering Custom Hooks in React: A Comprehensive Guide to Building, Using, and Optimizing Your Hooks

Updated on 06/09/2024421 Views

Introduction

In the world of web development, React is a highly efficient JavaScript library used to build user interfaces. React Hooks has revolutionized how developers handle state and lifecycle features in functional components. While built-in hooks like ‘useState’ and ‘useEffect’ offer substantial functionality, they sometimes fail to handle complex scenarios. This is where custom hooks come into play. They allow developers to write reusable, maintainable, and modular code.

Overview

Custom hooks in React provide a powerful mechanism to abstract component logic into reusable functions. This guide delves into custom hooks and demonstrates how they can be crafted to enhance code readability and reduce redundancy. From basic to advanced uses, we will explore how custom hooks can be tailored to specific needs, to streamline React projects.

The Basics of React Custom Hooks

Whether you are a novice or an experienced developer, let’s understand the basics of how to create custom hooks, go through practical examples, and discover best practices to test and deploy them in real-world applications.

A. Explanation of a Custom Hook in React JS In React, a custom hook is a JavaScript function that starts with 'use' and may call other hooks. It's a pattern that allows you to reuse stateful logic across multiple components without changing their structure. This means you can package part of a component's behavior into reusable chunks and share that logic across the application.Consider a custom hook called 'useFormInput' that manages form input states and handlers. Here's how to implement it:

import { useState } from 'react';

function useFormInput(initialValue) {

  const [value, setValue] = useState(initialValue);

function handleChange(e) {

    setValue(e.target.value);

  }

return {

    value,

    onChange: handleChange,

  };

}

export default useFormInput;

This hook can be used in any component to handle form inputs.

import React from 'react';

import useFormInput from './useFormInput';

function Form() {

  const email = useFormInput('');

  const password = useFormInput('');

function handleSubmit(event) {

    event.preventDefault();

    alert(`Email: ${email.value}, Password: ${password.value}`);

  }

return (

    <form onSubmit={handleSubmit}>

      <label>

        Email:

        <input type="text" {...email} />

      </label>

      <label>

        Password:

        <input type="password" {...password} />

      </label>

      <button type="submit">Submit</button>

    </form>

  );

}

export default Form;

B. Comparison with Built-in HooksBuilt-in hooks like 'useState', 'useEffect', and 'useContext' provide foundational functionality in React. 'useState' is used to add state to functional components, and 'useEffect' is for side effects. Custom hooks build on these primitives and offer a way to encapsulate logic that utilizes basic hooks into reusable functions. This allows you to abstract component logic into cleaner, manageable code. 

C. When and Why to Consider Creating Custom Hooks

When to create custom hooks:

  • Repeating Logic: In case of same logic repetition across multiple components.
  • Complex Logic: When a component becomes complex with mixed logic that can be isolated.
  • Shareable Logic: If the logic within a component can potentially be used by other components or applications.

Why create custom hooks:

  • Reusability: Encapsulates and abstracts component logic making it reusable across different components.
  • Simplicity: Keeps the component clean and focuses more on the UI part, pushing the logic handling outside of the component.
  • Separation of Concerns: Helps in separating the UI from the state logic.

If you are implementing a feature that requires fetching data from an API, instead of repeating the fetch logic in every component, you can create a 'useFetch' hook that can be reused wherever needed.

import { useState, useEffect } from 'react';

function useFetch(url) {

  const [data, setData] = useState(null);

  const [loading, setLoading] = useState(true);

  const [error, setError] = useState(null);

useEffect(() => {

    const fetchData = async () => {

      try {

        const response = await fetch(url);

        const result = await response.json();

        setData(result);

        setLoading(false);

      } catch (error) {

        setError(error);

        setLoading(false);

      }

    };

fetchData();

  }, [url]);

return { data, loading, error };

}

This hook can be used in any component to fetch data. It simplifies components and maintains consistency across projects.

Guide to Create a Simple Custom Hook ('useFormInput')

Creating custom hooks in React can simplify component logic, particularly for tasks like handling form inputs. Here is the detailed process of setting up a simple custom hook called 'useFormInput', to manage the state and changes of form inputs.

  • Step 1: Setup Your React Environment
    Create a new React project using Create React App:

npx create-react-app my-custom-hook-app

cd my-custom-hook-app

  • Step 2: Create a Custom Hook File
    Create a new file for your custom hook, e.g., 'useFormInput.js' in the 'src' folder.
  • Step 3: Import 'useState' from React
    Since we'll manage the input state, we need 'useState'.

import { useState } from 'react';

  • Step 4: Define the 'useFormInput' HookThis function initializes the state with the provided initial value and manages updates:

function useFormInput(initialValue) {

  const [value, setValue] = useState(initialValue);

function handleChange(e) {

    setValue(e.target.value);

  }

return {

    value,

    onChange: handleChange,

  };

}

export default useFormInput;

Code Example and Detailed Walkthrough

File: 'useFormInput.js'

import { useState } from 'react';

function useFormInput(initialValue) {

  const [value, setValue] = useState(initialValue);

function handleChange(e) {

    setValue(e.target.value);

  }

  return {

    value,

    onChange: handleChange,

  };

}

export default useFormInput;

Explanation:

  • 'useState(initialValue)': Initializes the state to hold the value of the form input.
  • 'handleChange': This function is triggered on the 'onChange' event of the input field, updating the state with the input's current value.
  • The hook returns an object containing 'value' and 'onChange', which can be spread directly onto the form input.

Using the 'useFormInput' Hook in a ComponentLet’s use this custom hook in a functional component to handle form inputs.

File: 'App.js'

import React from 'react';

import useFormInput from './useFormInput';

function App() {

  const name = useFormInput(''); // Initialize with empty string

  const age = useFormInput('');  // Initialize with empty string

function handleSubmit(event) {

    event.preventDefault();

    alert(`Name: ${name.value}, Age: ${age.value}`);

  }

  return (

    <form onSubmit={handleSubmit}>

      <div>

        <label>Name:</label>

        <input type="text" {...name} />

      </div>

      <div>

        <label>Age:</label>

        <input type="text" {...age} />

      </div>

      <button type="submit">Submit</button>

    </form>

  );

}

export default App;

  • Form Setup: The 'App' component uses the 'useFormInput' hook for two form fields: 'name' and 'age'.
  • Using the Hook: Each call to 'useFormInput' manages its state, making the form inputs-controlled components.
  • Spread Attributes: '{...name}' and '{...age}' spread the 'value' and 'onChange' properties onto the respective inputs, linking them to the state managed by our custom hook.
  • Handling Submission: On submitting the form, it alerts the values entered.

This approach ensures that each input's state management is encapsulated within the ‘useFormInput’ hook, reducing boilerplate and increasing reusability across your React application.

Creating Advanced Custom Hooks in React

Advanced custom hooks in React allow the encapsulation and reuse of complex functionalities across projects. They integrate multiple built-in hooks, manage significant logic, and optimize performance. 

Let's explore how to create a complex custom hook that involves multiple hooks and performance considerations.

Combining Multiple Built-in Hooks to Create a Complex Custom Hook

A powerful aspect of custom hooks is their ability to combine several built-in hooks to handle complex functionalities. This makes the code reusable and keeps component logic clean and maintainable.

Example: 'useUserProfile'

This custom hook might combine 'useState', 'useEffect', and 'useContext' to fetch, display, and manage a user profile.

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

import { UserContext } from './UserContext';

import { fetchUserProfile } from './api';

function useUserProfile(userId) {

  const [profile, setProfile] = useState(null);

  const [isLoading, setIsLoading] = useState(false);

  const [error, setError] = useState(null);

  const { token } = useContext(UserContext);

useEffect(() => {

    setIsLoading(true);

    fetchUserProfile(userId, token)

      .then(profile => {

        setProfile(profile);

        setIsLoading(false);

      })

      .catch(err => {

        setError(err);

        setIsLoading(false);

      });

  }, [userId, token]);

return { profile, isLoading, error };

}

  • 'useState' is used to manage the state of the user profile, loading status, and any errors.
  • 'useEffect' performs the side effect of fetching user data when the component mounts or when the 'userId' or 'token' changes.
  • 'useContext' accesses the global user context, which might include authentication tokens needed for secure API calls.

Performance Considerations and Optimizations

When developing custom hooks, consider their performance impact, especially regarding unnecessary re-renders and memory leaks.

  • Memoization: Use 'useMemo' and 'useCallback' to prevent unnecessary recalculations and re-renders.
  • Dependency management: Carefully manage dependencies in 'useEffect' to avoid excessive or missing updates.
  • Cleanup functions: Always provide cleanup functions in 'useEffect' to handle component unmount scenarios, preventing memory leaks.

Example: 'useAuth' for Handling Authentication

Handling authentication is a common requirement for many applications. A 'useAuth' hook can manage authentication state, and user data, and provide methods for signing in and signing out.

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

import { authAPI, verifyToken } from './authenticationService';

function useAuth() {

  const [user, setUser] = useState(null);

  const [error, setError] = useState(null);

  const [isLoading, setIsLoading] = useState(false);

useEffect(() => {

    const token = localStorage.getItem('token');

    if (token) {

      verifyToken(token)

        .then(user => setUser(user))

        .catch(err => {

          console.error(err);

          setError("Authentication failed");

          localStorage.removeItem('token');

        });

    }

  }, []);

const login = async (username, password) => {

    setIsLoading(true);

    try {

      const user = await authAPI.login(username, password);

      localStorage.setItem('token', user.token);

      setUser(user);

      setIsLoading(false);

    } catch (error) {

      setError(error);

      setIsLoading(false);

    }

  };

const logout = () => {

    localStorage.removeItem('token');

    setUser(null);

  };

return { user, login, logout, error, isLoading };

}

  • The hook uses 'useState' to manage the user's authentication status and any relevant errors, and uses 'useEffect' to check if a user is already logged in (via a token in localStorage) when the component mounts.
  • Functions 'login' and 'logout' handle user authentication actions. They update the user's state and manage the authentication token in localStorage.

This 'useAuth' hook provides a modular and reusable way to integrate authentication throughout React applications. This ensures components that depend on user authentication can easily manage and respond to changes in authentication status.

Final Words 

Custom hooks in React provide an easy and simplified reuse functionality across projects. They help manage simple form inputs and handle complex authentication processes. Custom hooks enhance modularity and maintainability. By leveraging hooks, you can keep your components clean and focused, optimize performance, and accelerate your development process. As you advance, experimenting with and adopting custom hooks will elevate your React applications, making them efficient and easier to manage.

FAQs

1. What are custom hooks in React?

Custom hooks in React are functions that let you hook into React state and lifecycle features from function components. They allow creation of reusable stateful logic that can be shared across multiple components.

2. What is a custom hook name in React?

In React, a custom hook name typically starts with 'use', followed by a description of its functionality, such as 'useFormInput' or 'useFetch'. This naming convention helps indicate that the function is a hook and adheres to React's rules of hooks.

3. What is the difference between custom hooks and HOCs?

Custom hooks and Higher-Order Components (HOCs) allow for the reusability of logic in React, differently. Custom hooks enable you to use React state and lifecycle features within function components through a hook function. They provide a more modular and flexible way to share logic by encapsulating it in functions that can be called within other functional components.

HOCs involve taking a component and returning a new component with extended or altered behavior. They wrap a component to inject additional props or alter its behavior, which can sometimes lead to more complex component hierarchies and less transparent code flow.

4. What are the 4 hooks in React?

The 4 fundamental hooks in React are:

i) 'useState': Manages state in functional components.
ii) 'useEffect': Handles side effects in functional components.
iii) 'useContext': This enables you to consume context values efficiently.
iv) 'useReducer': Offers more complex state logic than 'useState', similar to Redux.

5. Where do custom hooks React?

Custom hooks in React are used within functional components to encapsulate and reuse logic related to state, side effects, or other React features, making the code more modular and manageable.

Kechit Goyal

Kechit Goyal

Team Player and a Leader with a demonstrated history of working in startups. Strong engineering professional with a Bachelor of Technology (BTech…Read More

Talk to Career Expert
form image
+91
*
By clicking, I accept theT&Cand
Privacy Policy
image
Join 10M+ Learners & Transform Your Career
Learn on a personalised AI-powered platform that offers best-in-class content, live sessions & mentorship from leading industry experts.
right-top-arrowleft-top-arrow

upGrad Learner Support

Talk to our experts. We’re available 24/7.

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918045604032

Disclaimer

upGrad does not grant credit; credits are granted, accepted or transferred at the sole discretion of the relevant educational institution offering the diploma or degree. We advise you to enquire further regarding the suitability of this program for your academic, professional requirements and job prospects before enr...