Lesson 10 — Utility Custom Hooks | useToggle, useDebounce

🧭 Introduction

Once you understand what makes a good custom hook, the next step is to build small, reusable utility hooks.

These hooks:

  • Solve common problems
  • Keep components clean
  • Are used across almost every real-world React project

In this lesson, we’ll build two industry-standard utility hooks:

  • useToggle
  • useDebounce

And more importantly — understand why they’re designed this way.


🎯 What You’ll Learn in This Lesson

By the end of this lesson, you will understand:

  • How to design simple utility hooks
  • How to avoid unnecessary re-renders
  • How to manage timing with hooks
  • Real-world usage of useToggle and useDebounce

🔁 useToggle — Managing Boolean State

❓ The Problem

Many components need:

  • Open / close
  • Show / hide
  • Enable / disable

Using useState repeatedly:

const [open, setOpen] = useState(false);

Logic gets duplicated everywhere.


🧩 Building useToggle

✅ Custom Hook

import { useState, useCallback } from "react";

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

  const toggle = useCallback(() => {
    setValue(v => !v);
  }, []);

  return [value, toggle];
}


🧠 Why This Design?

  • Uses useCallback → stable function reference
  • Uses functional update → safe state change
  • Simple API → [value, toggle]

🔍 Using useToggle

function Modal() {
  const [open, toggle] = useToggle(false);

  return (
    <>
      <button onClick={toggle}>Toggle</button>
      {open && <div className="modal">Modal</div>}
    </>
  );
}

Clean. Readable. Reusable.


🔁 useDebounce — Handling Rapid Changes

❓ The Problem

When users:

  • Type in search box
  • Resize window
  • Scroll

Events fire too frequently.


🧠 What is Debouncing?

Debouncing:

Delays execution until the user stops triggering events

Used for:

  • Search inputs
  • API calls
  • Validation

🧩 Building useDebounce

import { useEffect, useState } from "react";

function useDebounce(value, delay = 500) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
}


🧠 Why This Works

  • useEffect watches value changes
  • Timer delays update
  • Cleanup prevents memory leaks
  • State updates only when user pauses

🔍 Using useDebounce

function Search() {
  const [query, setQuery] = useState("");
  const debouncedQuery = useDebounce(query, 400);

  useEffect(() => {
    fetch(`/api/search?q=${debouncedQuery}`);
  }, [debouncedQuery]);

  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
    />
  );
}

👉 API calls happen only when typing stops.


⚠ Common Mistakes

❌ Mistake 1: Debouncing Inside Component

Leads to repeated logic and bugs.


❌ Mistake 2: Missing Cleanup

Timers must be cleared to avoid memory leaks.


❌ Mistake 3: Using Debounce for Everything

Not all problems need debouncing.


🧠 useToggle vs useDebounce (Mental Model)

HookPurpose
useToggleBoolean UI state
useDebounceRate-limiting updates

Both:

  • Improve performance
  • Improve readability

🔍 Real-World Utility Hooks (Bonus)

Professional codebases often include:

  • usePrevious
  • useThrottle
  • useLocalStorage
  • useMediaQuery

All follow the same design principles.


🎯 Best Practices

✅ Keep utility hooks small
✅ Give clear, simple APIs
✅ Avoid hidden side effects
✅ Write hooks once, reuse everywhere


❓ FAQs — Utility Hooks

🔹 Should utility hooks be generic?

Yes — avoid business-specific logic.


🔹 Can hooks call other hooks?

Yes — composition is powerful.


🔹 Are utility hooks always worth it?

Only when logic repeats.


🔹 Should utility hooks be tested?

Yes — they’re easy to test.


🧠 Quick Recap

✔ useToggle simplifies boolean state
✔ useDebounce controls rapid changes
✔ Utility hooks improve performance
✔ Clean APIs matter
✔ Reusability is the goal


🎉 Conclusion

Utility hooks are small tools with big impact.

Once you build them:

  • Your components become cleaner
  • Your logic becomes reusable
  • Your apps scale better

This lesson completes the utility hooks foundation ⚛️🧠


👉 Next Lesson

Lesson 11 — API Custom Hooks (useFetch / useAsync)

Leave a Comment