Skip to content

Understanding Custom Hooks in React

Posted on:January 29, 2026 at 07:05 PM

Hey there! If you’ve been coding in React for a while, you might have noticed your components getting a bit… chunky. You know what I mean. A bunch of useState here, a couple of useEffect there, and suddenly your component logic looks like a bowl of spaghetti.

I’ve been there too. Back when I started, I used to stuff everything inside the component. But then I discovered Custom Hooks, and it changed the game.

The Pain Point: Duplicated Logic

Imagine you have two components that need to fetch data from an API. You’d probably write the same useEffect logic in both of them.

Here is what it usually looks like:

import { useState, useEffect } from "react";

function UserProfile({ userId }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetch(`https://api.example.com/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [userId]);

  if (loading) return <p>Loading...</p>;
  return <div>{data.name}</div>;
}

See that fetching logic? If you have another component getting product details, you’d copy-paste that code. Not cool, right?

The Solution: Custom Hooks

A Custom Hook is basically just a JavaScript function that starts with use and can call other Hooks. That’s it. No magic.

Let’s extract that logic into a useFetch hook.

import { useState, useEffect } from "react";

// This is our custom hook
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

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

  return { data, loading };
}

I just moved the logic out. Now, let’s see how our component looks.

function UserProfile({ userId }) {
  // Look how clean this is!
  const { data, loading } = useFetch(`https://api.example.com/users/${userId}`);

  if (loading) return <p>Loading...</p>;
  return <div>{data?.name}</div>;
}

Why Should You Care?

  1. Reusability: Write once, use everywhere.
  2. Readability: Your components focus on UI, not logic.
  3. Testing: You can test the hook independently.

Final Thoughts

Custom Hooks might sound intimidating at first, but they are just functions. Start small. Next time you see duplicated logic or a huge useEffect, try pulling it out.

Happy coding!