63 lines
1.5 KiB
TypeScript
63 lines
1.5 KiB
TypeScript
|
|
import { useState, useEffect, useCallback } from 'react';
|
||
|
|
|
||
|
|
interface UseApiOptions {
|
||
|
|
autoFetch?: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface UseApiResult<T> {
|
||
|
|
data: T | null;
|
||
|
|
loading: boolean;
|
||
|
|
error: string | null;
|
||
|
|
refetch: () => Promise<void>;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useApi<T>(
|
||
|
|
url: string,
|
||
|
|
options: UseApiOptions = { autoFetch: true }
|
||
|
|
): UseApiResult<T> {
|
||
|
|
const [data, setData] = useState<T | null>(null);
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
const [error, setError] = useState<string | null>(null);
|
||
|
|
|
||
|
|
const fetchData = useCallback(async () => {
|
||
|
|
setLoading(true);
|
||
|
|
setError(null);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const response = await fetch(url);
|
||
|
|
if (!response.ok) {
|
||
|
|
const errorData = await response.json();
|
||
|
|
throw new Error(errorData.error || 'Failed to fetch data');
|
||
|
|
}
|
||
|
|
const result = await response.json();
|
||
|
|
setData(result);
|
||
|
|
} catch (err) {
|
||
|
|
setError(err instanceof Error ? err.message : 'An error occurred');
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
}, [url]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (options.autoFetch) {
|
||
|
|
fetchData();
|
||
|
|
}
|
||
|
|
}, [fetchData, options.autoFetch]);
|
||
|
|
|
||
|
|
return { data, loading, error, refetch: fetchData };
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function apiCall<T>(
|
||
|
|
url: string,
|
||
|
|
options?: RequestInit
|
||
|
|
): Promise<T> {
|
||
|
|
const response = await fetch(url, options);
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
const errorData = await response.json();
|
||
|
|
throw new Error(errorData.error || 'API call failed');
|
||
|
|
}
|
||
|
|
|
||
|
|
return response.json();
|
||
|
|
}
|