As React applications grow, developers quickly discover a recurring problem: the same API consumption logic is repeated across multiple components.

Technical Blog Article — App 69: Building a Reusable Custom Fetch Hook with React, TypeScript, Fluent UI, and Vite

Introduction

As React applications grow, developers quickly discover a recurring problem: the same API consumption logic is repeated across multiple components.

Every component that loads data typically needs:

  • a loading state
  • an error state
  • a fetch request
  • a useEffect hook
  • data storage
  • response handling

Initially this duplication may seem harmless, but in enterprise applications it creates technical debt, increases maintenance costs, and makes applications harder to evolve.

In App 69 — Custom Fetch Hook, we solve this problem using one of React’s most powerful architectural features:

Custom Hooks.

This application belongs to Block 4 — Effects and Architecture, where the focus moves beyond basic components and enters professional React architecture patterns. The objective is to understand how React allows developers to extract reusable stateful logic into custom hooks while keeping UI components clean and focused on presentation.

The central concept introduced in this application is:

UI Components
Custom Hook
API

Instead of every component implementing its own data-loading behavior, a reusable hook becomes responsible for communication with external systems.

This follows the official React Learn guidance regarding Custom Hooks and logic reuse.


Why Custom Hooks Exist

Consider a project with:

  • User List
  • Product Catalog
  • Dashboard
  • Customer Directory
  • Reports Page

Without a reusable hook, each page might contain:

useEffect(...)
fetch(...)
loading state
error state

repeated over and over again.

The result is:

Duplicate code
Duplicate bugs
Duplicate maintenance

React solves this through Custom Hooks.

A custom hook allows us to encapsulate stateful behavior inside a reusable function.

Instead of writing:

API logic
API logic
API logic
API logic

we write:

useFetch()

once and reuse it everywhere.


Understanding React Effects

The application uses:

useEffect()

inside the custom hook.

This is the correct place for Effects.

According to React Learn:

Effects synchronize React components with external systems.

An API is an external system.

Therefore:

Fetching data
=
Effect

This makes useEffect appropriate here.

Unlike previous applications where effects were unnecessary, this app demonstrates a legitimate use case.


Project Creation

Create the project:

mkdir bloco04
cd bloco04
npm create vite@latest app69-custom-fetch-hook -- --template react-ts
cd app69-custom-fetch-hook
npm install
npm install @fluentui/react-components

Create folders:

mkdir src\components
mkdir src\hooks
mkdir src\models
mkdir src\services
mkdir src\styles

Create files:

New-Item src\hooks\useFetch.ts -ItemType File
New-Item src\models\User.ts -ItemType File
New-Item src\components\UserCard.tsx -ItemType File
New-Item artigo.md -ItemType File

Project Architecture

The final architecture is:

src/
├── components/
│ └── UserCard.tsx
├── hooks/
│ └── useFetch.ts
├── models/
│ └── User.ts
├── services/
├── styles/
├── App.tsx
├── main.tsx
└── index.css

Notice how the architecture separates concerns.

The component displays information.

The hook loads information.

The model defines information.

Each file has a single responsibility.


Creating the User Model

The model defines the structure of the API response.

export interface User {
id: number;
name: string;
email: string;
phone: string;
website: string;
}

This interface provides:

  • strong typing
  • autocomplete
  • compile-time validation
  • safer refactoring

Instead of guessing API structure, TypeScript knows exactly what data exists.


Creating the Custom Hook

The heart of the application is:

export function useFetch<T>(url: string)

Notice the generic:

<T>

This is extremely important.

The hook is not limited to users.

It can fetch:

  • users
  • products
  • orders
  • reports
  • tickets
  • customers

The hook becomes reusable across the entire application.


Understanding Generic Types

Without generics:

useFetchUsers()

would only work for users.

With generics:

useFetch<User[]>()
useFetch<Product[]>()
useFetch<Order[]>()

the same hook works everywhere.

This is one of the most powerful TypeScript features for React architecture.


Understanding State Management

Inside the hook we create three states:

data
loading
error

These represent the complete API lifecycle.

Loading

Before the request completes:

loading = true

The UI can show:

<Spinner />

Success

When the request succeeds:

data contains response
loading = false

The UI displays results.


Error

When something fails:

error contains message
loading = false

The UI displays feedback.

This pattern appears in nearly every professional React application.


Why Components Become Cleaner

Without a hook:

UserPage
loading state
error state
useEffect
ProductPage
loading state
error state
useEffect
Dashboard
loading state
error state
useEffect

Huge duplication.

With a hook:

const {
data,
loading,
error,
} = useFetch(...)

The component becomes focused on presentation.

This separation of concerns improves:

  • readability
  • maintainability
  • scalability
  • testing

Fluent UI Integration

The UI uses Fluent UI components:

  • Card
  • Text
  • Title
  • Spinner

These components automatically provide:

  • Microsoft styling
  • accessibility
  • keyboard support
  • enterprise design consistency

This allows us to focus on React architecture rather than manually building visual controls.


Understanding the UserCard Component

The UserCard component is intentionally simple.

Its responsibility is:

Receive user data
Display user data

Nothing more.

Notice it does not:

  • fetch data
  • call APIs
  • manage loading
  • manage errors

This follows React’s composition philosophy.

Components should have clear responsibilities.


Separation of Concerns

The architecture now looks like:

UserCard
Presentation
useFetch
Business Logic
API
External System

Each layer performs a different job.

This pattern scales naturally into enterprise applications.


React Mental Model

This application reinforces a fundamental React concept:

Effects synchronize
Components render
Hooks reuse logic

Many developers initially place all logic inside components.

As applications grow, that approach becomes difficult to maintain.

Custom Hooks solve this by extracting reusable behavior.


Why This App Matters

App 69 represents a major architectural milestone.

Earlier apps taught:

  • components
  • props
  • state
  • events
  • forms
  • effects

This app teaches:

Architecture

The ability to organize logic cleanly becomes increasingly important as applications become larger.

The same pattern introduced here will be reused in:

  • App 70 Context API
  • App 71 Favorites System
  • App 72 DataGrid API
  • App 73 Analytics Dashboard
  • App 74 Cryptocurrency Monitor
  • App 75 Repository Explorer

and many future enterprise applications.


Technical Summary

ConceptPurpose
Custom HookReuse stateful logic
useFetchGeneric API abstraction
useEffectSynchronize with API
useStateStore data lifecycle
TypeScript GenericsReusable typing
Fluent UIEnterprise UI
SpinnerLoading feedback
Error StateFailure handling
Separation of ConcernsClean architecture
Reusable LogicProfessional React pattern

Official Documentation

React

  • React Learn
  • Reusing Logic with Custom Hooks
  • Synchronizing with Effects
  • You Might Not Need an Effect

Fluent UI

  • Fluent UI React Components

TypeScript

  • TypeScript Generics
  • Interfaces
  • Type Safety

Vite

  • Vite Guide

Final Architectural Insight

The most important lesson from App 69 is that React applications should not duplicate stateful logic.

Instead of:

Component A → fetch
Component B → fetch
Component C → fetch

we create:

useFetch()

once.

Then reuse it everywhere.

This transforms React from a collection of components into a scalable architecture.

The mental model becomes:

External System
Custom Hook
React State
UI Components
Rendered Interface

That transition—from writing components to designing reusable architecture—is what separates intermediate React development from professional React engineering.

Current Progress

BlockAppNameStatus
Block 461REST API ConsumerCompleted
Block 462API DashboardCompleted
Block 463Async SearchCompleted
Block 464GitHub ExplorerCompleted
Block 465Weather AppCompleted
Block 466Pagination SystemCompleted
Block 467Infinite ScrollCompleted
Block 468Data CacheCompleted
Block 469Custom Fetch HookCurrent
Block 470Global State with ContextNext

Roadmap based on the ReactLab 100 Apps structure.

Edvaldo Guimrães Filho Avatar

Published by