Custom Hooks

After building the POS system for a few weeks, I noticed I was copying and pasting the same data-fetching pattern everywhere: set loading to true, fetch data, handle errors, set loading to false. Every product list, order history, and category selector had identical logic with different URLs. I had the same cart operations duplicated across components. The code worked, but it violated the DRY principle so hard it hurt.

That's when I discovered custom hooksβ€”the ability to extract reusable stateful logic into functions. Not just any functions, but functions that can use hooks themselves. I turned 200 lines of repeated fetch logic into a single useProducts hook. Cart operations became useCart. Authentication became useAuth. My components went from 100 lines to 20.

What Are Custom Hooks?

Custom hooks are JavaScript functions that use React hooks. They let you extract component logic into reusable functions.

Rules:

  1. Name must start with use (e.g., useProducts, useCart)

  2. Can call other hooks (useState, useEffect, useContext)

  3. Must follow the Rules of Hooks (only call at top level)

When to create a custom hook:

  • You're copying the same stateful logic across components

  • Complex logic is making your component hard to read

  • You want to share logic across your app

In the POS system, I created hooks for:

  • useProducts - Fetching and filtering products

  • useCart - Cart operations

  • useAuth - Authentication state and actions

  • useLocalStorage - Persisting state

  • useDebounce - Debouncing search input

Your First Custom Hook: useProducts

Let's extract the product-fetching logic into a reusable hook.

Before: Repeated in Every Component

Problems: Duplicated logic, hard to maintain, easy to introduce bugs.

After: Single Reusable Hook

Using the Hook

Benefits:

  • DRY (Don't Repeat Yourself)

  • Easier to test

  • Easier to maintain

  • Components stay focused on rendering

Building useCart Hook

Let's extract cart operations into a reusable hook.

Using useCart

Building useAuth Hook

Authentication logic is another great candidate for a custom hook.

Using useAuth

Building useLocalStorage Hook

A general-purpose hook for persisting state:

Using useLocalStorage

Building useDebounce Hook

For search inputs that shouldn't trigger API calls on every keystroke:

Using useDebounce

Without debounce: Type "chicken" β†’ 7 API calls (c, ch, chi, chic, chick, chicke, chicken)

With debounce: Type "chicken" β†’ 1 API call (after you stop typing)

Composing Hooks Together

Hooks can use other hooks. Here's a powerful pattern:

Using the Composed Hook

The component is so clean! All the complex logic is in the hook.

Rules of Hooks

These are CRITICAL and React will break if you violate them:

1. Only Call Hooks at the Top Level

2. Only Call Hooks from React Functions

3. Custom Hooks Must Start with "use"

Real POS Example: Complete Hook Setup

Here's how I organize hooks in the POS system:

Common Mistakes to Avoid

1. Not Following Hook Rules

2. Forgetting Dependencies

3. Not Using useCallback for Functions

4. Overusing Custom Hooks

Key Learnings

  1. Custom hooks extract reusable stateful logic

  2. Name must start with "use" (useProducts, useCart)

  3. Can use other hooks (useState, useEffect, other custom hooks)

  4. Follow Rules of Hooks (top level, React functions only)

  5. Return values that make sense for the hook's purpose

  6. Use useCallback for stable function references

  7. Compose hooks to build powerful abstractions

  8. Don't overuse - simple state can stay in components

Next Steps

You've mastered custom hooks for organizing logic. But the POS isn't a single pageβ€”it has products, cart, checkout, and order history screens. How do you navigate between them? In the next article, we'll add React Router to build a multi-page navigation system with protected routes.

Continue to: Routing β†’

Last updated