REACT Custom Hooks

Custom Hooks in React allow you to reuse stateful logic across multiple components. They are essentially JavaScript functions that use one or more React hooks (useState, useEffect, etc.) to encapsulate and share functionality. Custom Hooks help you keep your components clean and modular by abstracting repetitive logic.

Why Use Custom Hooks?

  1. Reusability: Encapsulate logic that can be shared across multiple components.
  2. Clean Code: Separate logic from component UI, making components easier to read and maintain.
  3. Abstraction: Keep components focused on their primary responsibility by moving complex logic into hooks.

Rules for Custom Hooks

  • Custom Hooks are named with the prefix use (e.g., useFetch, useLocalStorage).
  • They must follow the Rules of Hooks, such as:
    • Only call hooks at the top level.
    • Only call hooks inside React function components or other custom hooks.

Syntax of a Custom Hook

function useCustomHook(parameters) {
    // Use React hooks like useState, useEffect, etc.
    const [state, setState] = useState(initialValue);

    // Define logic here
    useEffect(() => {
        // Perform some operation
    }, [dependencies]);

    // Return data or functions for the component to use
    return [state, setState];
}

Try It Now

Example: Custom Hook for Fetching Data

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);
                if (!response.ok) {
                    throw new Error("Failed to fetch");
                }
                const result = await response.json();
                setData(result);
            } catch (err) {
                setError(err.message);
            } finally {
                setLoading(false);
            }
        };

        fetchData();
    }, [url]);

    return { data, loading, error };
}

export default useFetch;

Try It Now

 

Using the Custom Hook

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

function App() {
    const { data, loading, error } = useFetch("https://api.example.com/data");

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

    return (
        <div>
            <h1>Fetched Data:</h1>
            <ul>
                {data.map((item) => (
                    <li key={item.id}>{item.name}</li>
                ))}
            </ul>
        </div>
    );
}

export default App;

Try It Now

 

Example: Custom Hook for Managing Local Storage

import { useState } from "react";

function useLocalStorage(key, initialValue) {
    const [storedValue, setStoredValue] = useState(() => {
        try {
            const item = window.localStorage.getItem(key);
            return item ? JSON.parse(item) : initialValue;
        } catch (error) {
            console.error(error);
            return initialValue;
        }
    });

    const setValue = (value) => {
        try {
            const valueToStore = value instanceof Function ? value(storedValue) : value;
            setStoredValue(valueToStore);
            window.localStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
            console.error(error);
        }
    };

    return [storedValue, setValue];
}

export default useLocalStorage;

Try It Now

Using the Custom Hook

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

function App() {
    const [name, setName] = useLocalStorage("name", "");

    return (
        <div>
            <h1>Custom Hook Example: Local Storage</h1>
            <input
                type="text"
                placeholder="Enter your name"
                value={name}
                onChange={(e) => setName(e.target.value)}
            />
            <p>Stored Name: {name}</p>
        </div>
    );
}

export default App;

Try It Now

Example: Custom Hook for Window Resize

import { useState, useEffect } from "react";

function useWindowSize() {
    const [windowSize, setWindowSize] = useState({
        width: window.innerWidth,
        height: window.innerHeight,
    });

    useEffect(() => {
        const handleResize = () => {
            setWindowSize({
                width: window.innerWidth,
                height: window.innerHeight,
            });
        };

        window.addEventListener("resize", handleResize);
        return () => window.removeEventListener("resize", handleResize);
    }, []);

    return windowSize;
}

export default useWindowSize;

Try It Now

Using the Custom Hook

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

function App() {
    const { width, height } = useWindowSize();

    return (
        <div>
            <h1>Window Size</h1>
            <p>Width: {width}px</p>
            <p>Height: {height}px</p>
        </div>
    );
}

export default App;

Try It Now

 

Benefits of Custom Hooks

  1. Separation of Concerns: Keeps the UI logic and business logic separate.
  2. Reusability: Write logic once and use it across multiple components.
  3. Testability: Custom hooks are easier to test compared to large components.

 

Common Use Cases

  • Fetching Data: useFetch
  • Local Storage Management: useLocalStorage
  • Debouncing or Throttling: useDebounce, useThrottle
  • Event Listeners: useEventListener
  • Authentication Handling: useAuth
  • Responsive Design: useWindowSize, useMediaQuery
  • Form State Management: useForm

Key Takeaways

  • Custom Hooks allow you to extract reusable logic, making your components more concise and maintainable.
  • They are a powerful way to keep your React applications modular and organized.
  • Always adhere to the Rules of Hooks and ensure dependencies are properly managed.