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( url: string, initialParams: Record = {}, options: FetchOptions = {}, cacheOptions: CacheOptions = { cache: true } ) { const [data, setData] = useState(null); const [params, setParams] = useState>(initialParams); const [loading, setLoading] = useState(true); const [error, setError] = useState(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) => { // 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); 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('/api/your-endpoint'); // // With no caching (for data that changes frequently) // const { data, loading, error, refetch } = useStandardApiFetch( // '/api/your-endpoint', // {}, // {}, // { cache: false } // ); // // With specific revalidation time // const { data, loading, error, refetch } = useStandardApiFetch( // '/api/your-endpoint', // {}, // {}, // { revalidate: 60 } // Revalidate every 60 seconds // );