DEV Community

Cover image for Day 7 of #100DaysOfCode — Understanding Custom Hooks in React
M Saad Ahmad
M Saad Ahmad

Posted on

Day 7 of #100DaysOfCode — Understanding Custom Hooks in React

If you’ve ever found yourself reusing the same state logic across multiple React components, you’ve already discovered the perfect use case for custom hooks. Today, let’s break down what custom hooks are, why they matter, and how to build them with real-world examples.


What Exactly Are Custom Hooks?

Custom hooks are reusable JavaScript functions in React that allow you to extract component logic into standalone, sharable units. They always start with the prefix use (e.g., useAuth, useFetch) and internally leverage React’s default hooks like useState, useEffect, or others.

Examples:

  • useFetch
  • useToggle
  • useLocalStorage

Think of custom hooks as a way to package behavior, not UI.


🤔 Why Use Custom Hooks When React Already Has Hooks?

React’s built-in hooks are powerful—but repetitive logic across multiple components can quickly become messy. Custom hooks let you:

  • Encapsulate repetitive logic (fetching, debouncing, form handling, auth, etc.)
  • Keep components clean and focused on UI
  • Share logic across the entire app
  • Make your code more testable and modular

In short, custom hooks help you write cleaner, DRY-er, and more maintainable code.

Custom hooks help us:

  • Avoid repeating code
  • Keep components cleaner
  • Reuse logic anywhere
  • Organize complex logic in one place

How Logic Moves Into Custom Hooks

Any logic that we write inside a component can also live inside a custom hook:

  • useState (state)
  • useEffect (side effects)
  • Functions
  • Memoized values
  • Event handlers

Here’s an example pattern:

function useExample() {
  const [value, setValue] = useState(0);

  useEffect(() => {
    console.log("Value changed:", value);
  }, [value]);

  const increment = () => setValue(prev => prev + 1);

  return { value, increment };
}
Enter fullscreen mode Exit fullscreen mode

And in the component:

const { value, increment } = useExample();
Enter fullscreen mode Exit fullscreen mode

The component stays clean, and the logic stays reusable.


Returning Multiple Values

A custom hook can return anything — objects, arrays, functions, booleans, etc.

Example:

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

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

  return { count, increment, decrement };
}
Enter fullscreen mode Exit fullscreen mode

Using the Same Custom Hook in Multiple Components

This is the real power of custom hooks. One hook → infinite usage.

function CounterA() {
  const { count, increment } = useCounter(5);
  return <button onClick={increment}>A: {count}</button>;
}

function CounterB() {
  const { count, increment } = useCounter(10);
  return <button onClick={increment}>B: {count}</button>;
}
Enter fullscreen mode Exit fullscreen mode

Both use the same logic but keep separate state.


How To Create a Custom Hook

Rules:

  1. The name must start with use
  2. Hooks must be called at the top level (not inside loops or conditions)
  3. Hooks can be used inside components or custom hooks only

Simple Example: useToggle

import { useState } from "react";

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

  const toggle = () => setValue(v => !v);

  return [value, toggle];
}

export default useToggle;
Enter fullscreen mode Exit fullscreen mode

Usage in a component:

const [isOpen, toggleOpen] = useToggle();
Enter fullscreen mode Exit fullscreen mode

Reusing a Custom Hook in Multiple Components

Any component can simply import and call the hook:

export default function CounterUI() {
  const { count, increment, decrement } = useCounter();

  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Each component gets its own isolated state, even though the logic is shared.


Real-World Examples Where Custom Hooks Are Super Useful

1. Data Fetching – useFetch

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

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(json => {
        setData(json);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}
Enter fullscreen mode Exit fullscreen mode

2. LocalStorage Syncing – useLocalStorage

function useLocalStorage(key, initial) {
  const [value, setValue] = useState(() => {
    const saved = localStorage.getItem(key);
    return saved ? JSON.parse(saved) : initial;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [value]);

  return [value, setValue];
}
Enter fullscreen mode Exit fullscreen mode

Other Real-World Custom Hook Ideas

  • useDebounce for search inputs
  • useAuth for login/session handling
  • usePrevious for tracking previous values
  • useDarkMode for theme toggling
  • useOnlineStatus to detect internet connectivity

Conclusion

Custom hooks unlock one of React’s greatest strengths: the ability to share logic cleanly and efficiently. They help you build scalable apps, reduce repetition, and keep components focused on UI—not complexity.
Today’s topic might feel small, but mastering custom hooks will make a huge difference as your projects grow.

Happy coding!

Top comments (0)