React Custom Hooks and the Rules of Hooks

React’s power lies in its reusability and composability, and custom hooks are a key part of that. They allow developers to extract and reuse logic cleanly across components. But writing custom hooks requires understanding the Rules of Hooks, which ensure consistent and predictable behavior.

Let’s break this down.

🧩 What is a Custom Hook?

A custom hook is simply a JavaScript function whose name starts with use and that can call other hooks inside it.

✅ Why use custom hooks?

  • Extract reusable logic (e.g., fetching, form handling)

  • Keep components clean and focused

  • Avoid duplication

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    let isMounted = true;

    fetch(url)
      .then(res => res.json())
      .then(json => {
        if (isMounted) {
          setData(json);
          setLoading(false);
        }
      });

    return () => { isMounted = false; };
  }, [url]);

  return { data, loading };
}


const { data, loading } = useFetch('/api/posts');

Rules of Hooks

To maintain consistent behavior, React enforces some strict rules when using hooks:

1. Only Call Hooks at the Top Level

  • ✅ ✅ Good: Call hooks at the top of your component or custom hook.

  • ❌ Bad: Do not call them inside loops, conditions, or nested functions.

 

// Good
function MyComponent() {
  const [count, setCount] = useState(0);
}

// Bad
if (showCount) {
  const [count, setCount] = useState(0); // ❌ violates rules
}

Why? React relies on the order of hook calls being the same on every render.

2. Only Call Hooks from React Functions

  • Hooks can only be called from:

    • React function components

    • Custom hooks

// ✅ Okay inside a custom hook
function useCustom() {
  useEffect(() => {});
}

// ❌ Not allowed in regular JS functions
function doSomething() {
  useEffect(() => {}); // ❌
}

🧠 Tips for Writing Good Custom Hooks

  • Prefix your hook with useuseForm, useAuth, useTheme.

  • Keep them focused: one responsibility per hook.

  • Return values as an object {} or array [] depending on what’s more readable.

  • Document assumptions: Is it fetching once? Does it poll?


🔁 Reusing Hooks: Real World Example

Let’s create a useLocalStorage hook:

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

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

  return [value, setValue];
}

const [token, setToken] = useLocalStorage('auth_token', '');

🚨 What Happens If You Break the Rules?

If you violate the Rules of Hooks, React will throw errors like:

  • Rendered fewer hooks than expected

  • Invalid hook call

Dev Tools and the eslint-plugin-react-hooks package can help you avoid this.


Conclusion

React custom hooks are a powerful way to abstract and share logic between components. But they only work predictably when you follow the Rules of Hooks. Mastering this pattern helps make your codebase cleaner, more maintainable, and easier to test.

 

Leave a Reply

Your email address will not be published. Required fields are marked *