182 lines
4.8 KiB
TypeScript
182 lines
4.8 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
|
|
/**
|
|
* Cache options for the fetch request
|
|
*/
|
|
export type CacheOptions = {
|
|
/** Whether to cache the request (default: true) */
|
|
cache?: boolean;
|
|
/** Revalidate time in seconds (if not provided, uses Next.js defaults) */
|
|
revalidate?: number;
|
|
/** Force cache to be revalidated (equivalent to cache: 'no-store' in fetch) */
|
|
noStore?: boolean;
|
|
};
|
|
|
|
/**
|
|
* Request options for the fetch
|
|
*/
|
|
export type FetchOptions = {
|
|
/** HTTP method (default: 'GET') */
|
|
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
/** Request headers */
|
|
headers?: HeadersInit;
|
|
/** Request body (for POST, PUT, PATCH) */
|
|
body?: any;
|
|
};
|
|
|
|
/**
|
|
* A hook for fetching data from an API endpoint without pagination using Next.js fetch
|
|
* @param url The API endpoint URL
|
|
* @param initialParams Initial query parameters
|
|
* @param options Additional fetch options
|
|
* @param cacheOptions Cache control options
|
|
* @returns Object containing data, loading state, error state, and refetch function
|
|
*/
|
|
export function useStandardApiFetch<T>(
|
|
url: string,
|
|
initialParams: Record<string, any> = {},
|
|
options: FetchOptions = {},
|
|
cacheOptions: CacheOptions = { cache: true }
|
|
) {
|
|
const [data, setData] = useState<T | null>(null);
|
|
const [params, setParams] = useState<Record<string, any>>(initialParams);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<Error | null>(null);
|
|
|
|
/**
|
|
* Builds the URL with query parameters
|
|
*/
|
|
const buildUrl = () => {
|
|
const queryParams = new URLSearchParams();
|
|
|
|
// Add all non-null and non-empty params
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
if (value !== null && value !== '') {
|
|
queryParams.append(key, String(value));
|
|
}
|
|
});
|
|
|
|
const queryString = queryParams.toString();
|
|
return queryString ? `${url}?${queryString}` : url;
|
|
};
|
|
|
|
/**
|
|
* Configure fetch options including cache settings
|
|
*/
|
|
const getFetchOptions = (): RequestInit => {
|
|
const { method = 'GET', headers = {}, body } = options;
|
|
|
|
const fetchOptions: RequestInit = {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...headers,
|
|
},
|
|
};
|
|
|
|
// Add body for non-GET requests if provided
|
|
if (method !== 'GET' && body) {
|
|
fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
}
|
|
|
|
// Configure cache options
|
|
if (!cacheOptions.cache) {
|
|
fetchOptions.cache = 'no-store';
|
|
} else if (cacheOptions.noStore) {
|
|
fetchOptions.cache = 'no-store';
|
|
} else if (cacheOptions.revalidate !== undefined) {
|
|
fetchOptions.next = { revalidate: cacheOptions.revalidate };
|
|
}
|
|
|
|
return fetchOptions;
|
|
};
|
|
|
|
const fetchData = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const fullUrl = buildUrl();
|
|
const fetchOptions = getFetchOptions();
|
|
|
|
const response = await fetch(fullUrl, fetchOptions);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
}
|
|
|
|
const responseData = await response.json();
|
|
setData(responseData);
|
|
setError(null);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err : new Error('An unknown error occurred'));
|
|
setData(null);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, [url, JSON.stringify(params), JSON.stringify(options), JSON.stringify(cacheOptions)]);
|
|
|
|
/**
|
|
* Update the query parameters and trigger a refetch
|
|
* @param newParams New parameters to merge with existing ones
|
|
*/
|
|
const updateParams = (newParams: Record<string, any>) => {
|
|
// Filter out null or empty string values
|
|
const filteredParams = Object.entries(newParams).reduce((acc, [key, value]) => {
|
|
if (value !== null && value !== '') {
|
|
acc[key] = value;
|
|
}
|
|
return acc;
|
|
}, {} as Record<string, any>);
|
|
|
|
setParams(prev => ({
|
|
...prev,
|
|
...filteredParams
|
|
}));
|
|
};
|
|
|
|
/**
|
|
* Reset all parameters to initial values
|
|
*/
|
|
const resetParams = () => {
|
|
setParams(initialParams);
|
|
};
|
|
|
|
/**
|
|
* Manually trigger a refetch of the data
|
|
*/
|
|
const refetch = () => {
|
|
fetchData();
|
|
};
|
|
|
|
return {
|
|
data,
|
|
loading,
|
|
error,
|
|
updateParams,
|
|
resetParams,
|
|
refetch
|
|
};
|
|
}
|
|
|
|
// // Basic usage (with default caching)
|
|
// const { data, loading, error, refetch } = useStandardApiFetch<schema.YourDataType>('/api/your-endpoint');
|
|
|
|
// // With no caching (for data that changes frequently)
|
|
// const { data, loading, error, refetch } = useStandardApiFetch<schema.YourDataType>(
|
|
// '/api/your-endpoint',
|
|
// {},
|
|
// {},
|
|
// { cache: false }
|
|
// );
|
|
|
|
// // With specific revalidation time
|
|
// const { data, loading, error, refetch } = useStandardApiFetch<schema.YourDataType>(
|
|
// '/api/your-endpoint',
|
|
// {},
|
|
// {},
|
|
// { revalidate: 60 } // Revalidate every 60 seconds
|
|
// );
|