useFetch
A powerful hook for fetching data with built-in loading, error states, and request cancellation.
Live Demo
Try changing the user or post ID to see the hook automatically fetch new data. Toggle the skip option to prevent fetching.
👤 Fetch User Data
⏳ Loading...
📝 Fetch Post Data (with skip)
⏳ Loading...
📤 POST Request Example
Fill in the form and click Create Post
🛑 Request Abort Example
Start a slow fetch (5 second delay) and abort it before completion. The hook automatically cancels requests when unmounting or when a new request starts.
Click "Start Fetch" to begin
💡 Features Demonstrated:
- GET requests with automatic refetching on URL changes
- POST requests with custom headers and body
- Request abortion and cancellation
- Loading, error, and data states
- Skip option to prevent automatic fetching
- Manual refetch capability
Code Examples
Basic Usage
import { useFetch } from "@uiblock/hooks";
interface User {
id: number;
name: string;
email: string;
}
export default function UserProfile({ userId }: { userId: string }) {
const { data, loading, error } = useFetch<User>(
`https://api.example.com/users/${userId}`
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!data) return null;
return (
<div>
<h2>{data.name}</h2>
<p>{data.email}</p>
</div>
);
}With Custom Options
import { useFetch } from "@uiblock/hooks";
export default function PostsList() {
const { data, loading, error, refetch } = useFetch(
'https://api.example.com/posts',
{
method: 'GET',
headers: {
'Authorization': 'Bearer token123',
'Content-Type': 'application/json'
},
onSuccess: (data) => {
console.log('Data fetched successfully:', data);
},
onError: (error) => {
console.error('Fetch failed:', error);
}
}
);
return (
<div>
<button onClick={refetch}>Refresh</button>
{loading && <p>Loading...</p>}
{error && <p>Error: {error.message}</p>}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}Skip Fetching
import { useFetch } from "@uiblock/hooks";
import { useState } from "react";
export default function ConditionalFetch() {
const [shouldFetch, setShouldFetch] = useState(false);
const { data, loading } = useFetch(
'https://api.example.com/data',
{ skip: !shouldFetch }
);
return (
<div>
<button onClick={() => setShouldFetch(true)}>
Load Data
</button>
{loading && <p>Loading...</p>}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}Manual Refetch
import { useFetch } from "@uiblock/hooks";
export default function RefetchExample() {
const { data, loading, refetch } = useFetch(
'https://api.example.com/random'
);
return (
<div>
<button onClick={refetch} disabled={loading}>
{loading ? 'Loading...' : 'Get New Random Data'}
</button>
{data && <div>{JSON.stringify(data)}</div>}
</div>
);
}How It Works
Here's the implementation:
import { useState, useEffect, useCallback, useRef } from 'react'
export interface UseFetchOptions extends RequestInit {
skip?: boolean
onSuccess?: (data: any) => void
onError?: (error: Error) => void
}
export interface UseFetchResult<T> {
data: T | null
error: Error | null
loading: boolean
refetch: () => void
}
export function useFetch<T = any>(
url: string,
options: UseFetchOptions = {}
): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null)
const [error, setError] = useState<Error | null>(null)
const [loading, setLoading] = useState<boolean>(!options.skip)
const { skip, onSuccess, onError, ...fetchOptions } = options
const abortControllerRef = useRef<AbortController | null>(null)
const fetchData = useCallback(async () => {
if (skip) return
// Cancel previous request
if (abortControllerRef.current) {
abortControllerRef.current.abort()
}
abortControllerRef.current = new AbortController()
setLoading(true)
setError(null)
try {
const response = await fetch(url, {
...fetchOptions,
signal: abortControllerRef.current.signal
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const result = await response.json()
setData(result)
onSuccess?.(result)
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') {
return
}
const error = err instanceof Error ? err : new Error('An error occurred')
setError(error)
onError?.(error)
} finally {
setLoading(false)
}
}, [url, skip, onSuccess, onError])
useEffect(() => {
fetchData()
return () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort()
}
}
}, [fetchData])
return { data, error, loading, refetch: fetchData }
}API Reference
Parameters
url: stringThe URL to fetch data from
options?: UseFetchOptionsOptional configuration object
skip: Skip automatic fetchingonSuccess: Callback on successful fetchonError: Callback on error- All standard
RequestInitoptions (method, headers, body, etc.)
Return Value
data: T | nullThe fetched data, or null if not yet loaded
error: Error | nullError object if the fetch failed
loading: booleanTrue while the request is in progress
refetch: () => voidFunction to manually trigger a new fetch
💡 Use Cases
- Fetching user profiles and data
- Loading lists and collections
- API integration with loading states
- Conditional data fetching
- Real-time data refresh
- RESTful API consumption