React Custom Hooks Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    12 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of React Custom Hooks

Explain in Details and Show Important Info for Topic "React Custom Hooks" under 700 Words

Introduction to React Custom Hooks

What is a Custom Hook?

A custom hook is a JavaScript function whose name starts with use and can call other hooks. The use prefix is a convention that helps ensure that your function abides by the Rules of Hooks, which enforce that hooks are only called at the top level of the function, not inside loops, conditions, or nested functions. Custom hooks are not a feature of React; instead, they are a pattern enabled by Hooks.

Why Use Custom Hooks?

  1. Code Reusability: Custom hooks allow you to share stateful logic between components without changing their structure.
  2. Separation of Concerns: By isolating logic, you can make components easier to understand and modify.
  3. Improved Readability and Maintainability: Reducing the complexity of components can make them more readable and easier to maintain.
  4. Enhanced Team Collaboration: Commonly used hooks can be stored in a shared repository, making it easier for different parts of a team to interact with and understand application logic.

How to Create a Custom Hook

To create a custom hook, you start by defining a function that returns some data, possibly an array or object. The function should follow the use naming convention to indicate that it is a hook and can call other hooks.

Example: Creating a custom hook to fetch data

import { useState, useEffect } from 'react';

function useFetchData(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 data = await response.json();
        setData(data);
        setLoading(false);
      } catch (error) {
        setError(error);
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetchData;

How to Use a Custom Hook

Using a custom hook is straightforward. You simply call the hook from within a functional component or another custom hook, and destructure the returned data as needed.

Example: Using the useFetchData custom hook in a component

import React from 'react';
import useFetchData from './useFetchData';

function DataComponent() {
  const { data, loading, error } = useFetchData('https://api.example.com/data');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>Data</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

export default DataComponent;

Best Practices for Using Custom Hooks

  1. Naming Conventions: Always start custom hook names with use.
  2. Single Purpose: Keep custom hooks focused on a single piece of state or logic. This makes them easier to understand and reuse.
  3. No Side Effects: Avoid running side effects inside your custom hook, unless it's wrapped in one of the built-in Hooks like useEffect, useLayoutEffect, or useImperativeHandle.
  4. Avoid Nesting: While custom hooks can call other custom hooks, nesting too deep can make your code harder to follow.
  5. Documentation: Document your custom hooks clearly, including parameters and return values, to facilitate collaboration and reuse.

Conclusion

React Custom Hooks are a powerful feature that allows you to encapsulate and reuse logic within your React applications. By following best practices and understanding how to create and use custom hooks, you can write cleaner, more maintainable code that is easier to work with. As you continue to develop with React, custom hooks will be an invaluable tool in your toolkit.


Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement React Custom Hooks

Table of Contents

  1. Introduction to Custom Hooks
  2. Creating a Simple Custom Hook
  3. Using State in a Custom Hook
  4. Using Effects in a Custom Hook
  5. Combining Hooks

1. Introduction to Custom Hooks

What are Custom Hooks?

Custom Hooks in React let you extract component logic into reusable functions. While React comes with several built-in hooks like useState, useEffect, etc., you can also create your own hooks. A custom Hook is a JavaScript function whose name starts with use and that can call other Hooks.

Why Use Custom Hooks?

  • Reusability: By extracting logic into a custom hook, you can reuse it across different components.
  • Simplification: Custom hooks can make your components cleaner and more maintainable.
  • Encapsulation: You can encapsulate the logic and expose only what's necessary to other components.

2. Creating a Simple Custom Hook

Let's start with a very basic example. Imagine you have a simple counter component, and you want to extract the counter logic into a custom hook.

Component with Logic

import React, { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
            <button onClick={() => setCount(count - 1)}>Decrement</button>
        </div>
    );
}

export default Counter;

Create a Custom Hook

Let's extract the counter logic into a hook named useCounter.

import { useState } from 'react';

function useCounter(initialValue = 0) {
    const [count, setCount] = useState(initialValue);

    const increment = () => setCount(count + 1);
    const decrement = () => setCount(count - 1);

    return { count, increment, decrement };
}

export default useCounter;

Use the Custom Hook in Component

Now, use the useCounter hook in your Counter component.

import React from 'react';
import useCounter from './useCounter';

function Counter() {
    const { count, increment, decrement } = useCounter();

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
            <button onClick={decrement}>Decrement</button>
        </div>
    );
}

export default Counter;

Benefits

  • Reusability: You can use the useCounter hook in any other component where you need a counter.
  • Cleaner Code: The Counter component is now cleaner, and all the counter logic is encapsulated in the custom hook.

3. Using State in a Custom Hook

The previous example already covered using state in a custom hook with useState. Here's another example where we'll create a custom hook to manage form input state.

Form Component

import React, { useState } from 'react';

function Form() {
    const [formData, setFormData] = useState({ name: '', email: '' });

    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData((prev) => ({ ...prev, [name]: value }));
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Form Data:', formData);
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>Name:</label>
                <input
                    type="text"
                    name="name"
                    value={formData.name}
                    onChange={handleChange}
                />
            </div>
            <div>
                <label>Email:</label>
                <input
                    type="email"
                    name="email"
                    value={formData.email}
                    onChange={handleChange}
                />
            </div>
            <button type="submit">Submit</button>
        </form>
    );
}

export default Form;

Extract Logic into Custom Hook

Create a custom hook named useForm to manage form state.

import { useState } from 'react';

function useForm(initialState = {}) {
    const [values, setValues] = useState(initialState);

    const handleChange = (e) => {
        const { name, value } = e.target;
        setValues((prev) => ({ ...prev, [name]: value }));
    };

    const resetForm = () => {
        setValues(initialState);
    };

    return { values, handleChange, resetForm };
}

export default useForm;

Use the Custom Hook in Component

Now, use the useForm hook in your Form component.

import React from 'react';
import useForm from './useForm';

function Form() {
    const { values, handleChange, resetForm } = useForm({ name: '', email: '' });

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Form Data:', values);
        resetForm();
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>Name:</label>
                <input
                    type="text"
                    name="name"
                    value={values.name}
                    onChange={handleChange}
                />
            </div>
            <div>
                <label>Email:</label>
                <input
                    type="email"
                    name="email"
                    value={values.email}
                    onChange={handleChange}
                />
            </div>
            <button type="submit">Submit</button>
        </form>
    );
}

export default Form;

Benefits

  • Modularity: The form handling logic is now modular and can be reused across different form components.
  • Maintainability: Easier to maintain and less verbose components.

4. Using Effects in a Custom Hook

Effects in custom hooks can be used for side effects like data fetching, subscriptions, or manually changing the DOM from React components.

Fetching Data

Let's create a custom hook to fetch data from an API.

Component with Fetch Logic

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

function DataFetcher() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/posts/1')
            .then((response) => response.json())
            .then((data) => {
                setData(data);
                setLoading(false);
            })
            .catch((error) => {
                setError(error);
                setLoading(false);
            });
    }, []); // Empty dependency array for one-time fetch

    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;

    return (
        <div>
            <h1>{data.title}</h1>
            <p>{data.body}</p>
        </div>
    );
}

export default DataFetcher;

Extract Logic into Custom Hook

Create a custom hook named useFetchData.

import { useState, useEffect } from 'react';

function useFetchData(url) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        fetch(url)
            .then((response) => response.json())
            .then((data) => {
                setData(data);
                setLoading(false);
            })
            .catch((error) => {
                setError(error);
                setLoading(false);
            });
    }, [url]); // Dependency array for re-fetching when URL changes

    return { data, loading, error };
}

export default useFetchData;

Use the Custom Hook in Component

Now, use the useFetchData hook in your DataFetcher component.

import React from 'react';
import useFetchData from './useFetchData';

function DataFetcher() {
    const { data, loading, error } = useFetchData('https://jsonplaceholder.typicode.com/posts/1');

    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;

    return (
        <div>
            <h1>{data.title}</h1>
            <p>{data.body}</p>
        </div>
    );
}

export default DataFetcher;

Benefits

  • Reusability: You can use the useFetchData hook in any component where you need to fetch data from an API.
  • Maintainability: The API fetching logic is encapsulated, making it easier to maintain.

5. Combining Hooks

You can combine multiple custom hooks within a component to encapsulate different pieces of logic.

Example: A Todo App

Let's create a todo app that uses custom hooks for state management, form handling, and data fetching.

State Management Hook

Create a custom hook useTodoState to manage todo state.

import { useState } from 'react';

function useTodoState(initialTodos = []) {
    const [todos, setTodos] = useState(initialTodos);

    const addTodo = (todo) => {
        setTodos([...todos, todo]);
    };

    const removeTodo = (id) => {
        setTodos(todos.filter((todo) => todo.id !== id));
    };

    const toggleTodo = (id) => {
        setTodos(
            todos.map((todo) =>
                todo.id === id ? { ...todo, completed: !todo.completed } : todo
            )
        );
    };

    return { todos, addTodo, removeTodo, toggleTodo };
}

export default useTodoState;

Form Handling Hook

Create a custom hook useTodoForm to handle form input state.

import { useState } from 'react';

function useTodoForm(addTodo) {
    const [value, setValue] = useState('');

    const handleSubmit = (e) => {
        e.preventDefault();
        if (value.trim()) {
            addTodo({ id: Date.now(), text: value, completed: false });
            setValue('');
        }
    };

    return { value, setValue, handleSubmit };
}

export default useTodoForm;

Data Fetching Hook

Create a custom hook useFetchTodos to fetch todo data from an API.

import { useState, useEffect } from 'react';

function useFetchTodos(url) {
    const [data, setData] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        fetch(url)
            .then((response) => response.json())
            .then((data) => {
                setData(data);
                setLoading(false);
            })
            .catch((error) => {
                setError(error);
                setLoading(false);
            });
    }, [url]);

    return { data, loading, error };
}

export default useFetchTodos;

Todo App Component

Now, use all these custom hooks in your TodoApp component.

import React, { useMemo } from 'react';
import useTodoState from './useTodoState';
import useTodoForm from './useTodoForm';
import useFetchTodos from './useFetchTodos';
import TodoList from './TodoList';
import TodoForm from './TodoForm';

function TodoApp() {
    const { data, loading, error } = useFetchTodos('https://jsonplaceholder.typicode.com/todos?_limit=5');
    const { todos, addTodo, removeTodo, toggleTodo } = useTodoState(data);
    const { value, handleSubmit } = useTodoForm(addTodo);

    const memoizedTodos = useMemo(() => todos, [todos]);

    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;

    return (
        <div>
            <h1>Todo App</h1>
            <TodoForm handleSubmit={handleSubmit} value={value} />
            <TodoList
                todos={memoizedTodos}
                removeTodo={removeTodo}
                toggleTodo={toggleTodo}
            />
        </div>
    );
}

export default TodoApp;

Supporting Components

Create a TodoForm component.

import React from 'react';

function TodoForm({ handleSubmit, value }) {
    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                value={value}
                onChange={(e) => handleSubmit(e)}
                placeholder="Add new todo"
            />
            <button type="submit">Add</button>
        </form>
    );
}

export default TodoForm;

Create a TodoList component.

import React from 'react';

function TodoList({ todos, removeTodo, toggleTodo }) {
    return (
        <ul>
            {todos.map((todo) => (
                <li key={todo.id} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
                    {todo.text}
                    <button onClick={() => toggleTodo(todo.id)}>Toggle</button>
                    <button onClick={() => removeTodo(todo.id)}>Remove</button>
                </li>
            ))}
        </ul>
    );
}

export default TodoList;

Benefits

  • Modularity: Each piece of logic is encapsulated in its own custom hook.
  • Reusability: Hooks can be easily reused across different components.
  • Maintainability: Easier to maintain and test individual hooks.

Conclusion

Custom hooks are a powerful feature in React that allow you to encapsulate and reuse component logic. They help in breaking down your components into smaller, reusable pieces, making your code more organized and maintainable. The examples provided above should give you a good starting point to create and use custom hooks in your React applications.


References

Top 10 Interview Questions & Answers on React Custom Hooks

1. What is a Custom Hook in React?

Answer: A Custom Hook in React is a JavaScript function whose name starts with "use" and that can call other Hooks. Custom Hooks allow you to extract component logic into reusable functions. By convention, any function starting with "use" should follow the Rules of Hooks.

2. What are the Rules of Hooks in React?

Answer: There are two primary Rules of Hooks:

  • Only Call Hooks at the Top Level: Don't call Hooks inside loops, conditions, or nested functions. Always use Hooks at the top level of your React function to ensure they run in the same order each time a component renders.
  • Only Call Hooks from React Functions: Don't call Hooks from regular JavaScript functions. Instead, call Hooks from React function components and custom Hooks. You can build your own Custom Hooks to reuse stateful logic between different components.

3. How can I create a Custom Hook that manages form input states?

Answer: To create a Custom Hook that manages form input states, you can define a function that uses the useState Hook to keep track of input values. Here’s a simple example:

import { useState } from 'react';

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

  function handleChange(e) {
    setValue(e.target.value);
  }

  return {
    value,
    onChange: handleChange
  };
}

// Usage
function MyComponent() {
  const name = useFormInput('');
  const email = useFormInput('');

  return (
    <form>
      <input type="text" {...name} placeholder="Name" />
      <input type="email" {...email} placeholder="Email" />
      <input type="submit" />
    </form>
  );
}

4. When should you create a Custom Hook?

Answer: You should create a Custom Hook when you identify a piece of stateful logic that needs to be reused in multiple components. It helps in improving code readability, maintainability, and reduces duplication.

5. Can Custom Hooks have parameters?

Answer: Yes, Custom Hooks can accept parameters. Parameters help in making your Custom Hooks more dynamic and flexible. For example, the useFormInput Custom Hook could accept an initialValue and a validator function.

6. How can a Custom Hook share side-effects?

Answer: A Custom Hook can share side-effects using the useEffect Hook. useEffect allows you to perform side effects in function components, similar to lifecycle methods in class components.

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(json => setData(json));
  }, [url]); // URL is a dependency for useEffect

  return data;
}

// Usage
function MyComponent() {
  const post = useFetch('https://jsonplaceholder.typicode.com/posts/1');
  return <div>{post ? post.title : 'Loading...'}</div>;
}

7. Can a Custom Hook return multiple values?

Answer: Yes, a Custom Hook can return multiple values. It's common to return an object or an array from Custom Hooks to provide multiple pieces of state or methods.

function useCounter(initialValue) {
  const [count, setCount] = useState(initialValue);

  function increment() {
    setCount(count => count + 1);
  }

  function decrement() {
    setCount(count => count - 1);
  }

  return [count, increment, decrement];
}

// Usage
function MyComponent() {
  const [count, increment, decrement] = useCounter(0);
  return (
    <div>
      {count}
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}

8. Can I use state and lifecycle features of class components in Custom Hooks?

Answer: Yes, you can. Custom Hooks allow you to use features like state (useState), side effects (useEffect), context (useContext), and more from class components in a more modular way.

9. How can custom hooks simplify complex components?

Answer: Custom Hooks can simplify complex components by breaking down their logic into smaller, manageable pieces. This not only makes the original component cleaner but also allows the logic to be reused across other components in the app.

10. How do you debug Custom Hooks?

Answer: Debugging Custom Hooks is similar to debugging regular functions in React. Here are some tips:

  • Use React DevTools to inspect component state and props.
  • Add console.log statements within your Hooks to track the flow of data.
  • Use the useDebugValue Hook to display a label for Custom Hooks in React DevTools (useful for library maintainers).

You May Like This Related .NET Topic

Login to post a comment.