Intermediate⏱️ 8 min📘 Topic 8 of 13

🛠️ React Custom Hooks — Reuse Logic Without Wrappers

Build custom React hooks to reuse stateful logic — fetching, debouncing, localStorage, online status. Includes naming rules, gotchas and the interview answers companies want.

A custom hook is just a function whose name starts with use and that calls other hooks. That's it. The naming convention is what lets React (and ESLint) enforce the rules of hooks.

🧠 What hooks let you reuse

Before hooks, sharing stateful logic needed HOCs or render props (verbose, nested). Custom hooks let you extract logic into a plain function — components stay flat and clean.

🧪 Example: useToggle

function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn(o => !o), []);
  return [on, toggle];
}

// usage
const [open, toggleOpen] = useToggle();

🧪 Example: useDebounce

function useDebounce(value, delay = 300) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

📜 Rules still apply

  • Name MUST start with use
  • Call at top level only
  • Each component instance gets its own copy of the hook's state

💻 Code Examples

useLocalStorage — persist state across reloads

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

// usage
const [theme, setTheme] = useLocalStorage('theme', 'light');
Output:
Theme survives page reloads.

useOnlineStatus

function useOnlineStatus() {
  const [online, setOnline] = useState(navigator.onLine);
  useEffect(() => {
    const up = () => setOnline(true);
    const down = () => setOnline(false);
    window.addEventListener('online', up);
    window.addEventListener('offline', down);
    return () => {
      window.removeEventListener('online', up);
      window.removeEventListener('offline', down);
    };
  }, []);
  return online;
}
Output:
Returns live boolean — reacts to network status changes.

⚠️ Common Mistakes

  • Naming the hook without the `use` prefix — ESLint rules don't apply, hook order can break.
  • Calling a custom hook conditionally (`if (x) useThing()`) — violates rules of hooks.
  • Sharing state between instances by accident — each call gets its own state, not a singleton.
  • Putting business side effects in a hook AND in the component that uses it — duplicates work.

🎯 Interview Questions

Real questions asked at top product and service-based companies.

Q1.What's a custom hook?Beginner
A function whose name starts with `use` and that calls other hooks. Lets you extract and reuse stateful logic across components — without HOCs or render props.
Q2.Why must custom hook names start with `use`?Intermediate
It's a convention that React's lint plugin and runtime rely on to enforce the rules of hooks. Without the prefix, you could call hooks conditionally without warnings, which breaks how React tracks state by call order.
Q3.Do two components calling the same custom hook share state?Intermediate
No. Each call to a hook gives that component its own independent state. To share state, use Context or an external store inside the hook.
Q4.Give an example of useful custom hooks.Intermediate
useFetch, useDebounce, useLocalStorage, useOnlineStatus, useMediaQuery, useToggle, useInterval, usePrevious, useClickOutside, useKeyPress.
Q5.When should you NOT extract a custom hook?Advanced
When the logic is used in only one place — you're adding indirection without reuse. Extract once the same hook usage appears in 2+ components, or to clarify a complex effect's intent.

🧠 Quick Summary

  • Custom hook = function starting with `use` that uses other hooks.
  • Extract reusable stateful logic without HOCs/render props.
  • Each component instance gets its own state.
  • Useful: useDebounce, useLocalStorage, useFetch, useOnlineStatus.
  • Rules of hooks still apply inside custom hooks.