Files
prod-wag-backend-automate-s…/WebServices/client-frontend/src/components/common/hooks/useStandardApiFetch.ts

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
// );