react-antd-admin-panel
Version:
Modern TypeScript-first React admin panel builder with Ant Design 6
146 lines • 4.38 kB
JavaScript
import { useState, useEffect, useCallback, useRef } from 'react';
import { Get } from '../http/Get';
/**
* useGet - React hook for GET requests
*
* @template T - Response data type
* @param options - Hook options
* @returns Hook result with data, loading, error, and control functions
*
* @example
* ```tsx
* // Basic usage - fetches immediately
* const { data, loading, error } = useGet<User[]>({
* url: '/api/users',
* });
*
* // With parameters
* const { data, loading } = useGet<User[]>({
* url: '/api/users',
* params: { page: 1, limit: 10 },
* });
*
* // Manual execution
* const { data, execute, loading } = useGet<User>({
* url: '/api/users/1',
* immediate: false,
* });
* // Later: await execute();
*
* // Conditional fetch
* const { data } = useGet<User>({
* url: `/api/users/${userId}`,
* enabled: !!userId,
* });
* ```
*/
export function useGet(options) {
const { url, params, immediate = true, enabled = true, } = options;
const [data, setData] = useState(undefined);
const [loading, setLoading] = useState(immediate && enabled);
const [error, setError] = useState(undefined);
// Use ref to track the current Get instance for cancellation
const getInstanceRef = useRef(null);
// Track if component is mounted to prevent state updates after unmount
const mountedRef = useRef(true);
// Track the latest options to avoid stale closures
const optionsRef = useRef(options);
optionsRef.current = options;
/**
* Execute the GET request
*/
const execute = useCallback(async (overrideParams) => {
// Cancel any pending request
if (getInstanceRef.current) {
getInstanceRef.current.cancel();
}
const currentOptions = optionsRef.current;
// Create new Get instance
const get = new Get()
.target(currentOptions.url)
.params({ ...currentOptions.params, ...overrideParams });
if (currentOptions.headers) {
get.headers(currentOptions.headers);
}
getInstanceRef.current = get;
if (mountedRef.current) {
setLoading(true);
setError(undefined);
}
try {
const result = await get.execute();
if (mountedRef.current) {
setData(result);
setLoading(false);
currentOptions.onSuccess?.(result);
}
return result;
}
catch (err) {
const error = err instanceof Error ? err : new Error(String(err));
// Don't update state if request was aborted
if (error.name === 'CanceledError' || error.name === 'AbortError') {
return undefined;
}
if (mountedRef.current) {
setError(error);
setLoading(false);
currentOptions.onError?.(error);
}
return undefined;
}
}, []);
/**
* Abort the current request
*/
const abort = useCallback(() => {
if (getInstanceRef.current) {
getInstanceRef.current.cancel();
getInstanceRef.current = null;
}
if (mountedRef.current) {
setLoading(false);
}
}, []);
/**
* Reset state to initial values
*/
const reset = useCallback(() => {
abort();
if (mountedRef.current) {
setData(undefined);
setError(undefined);
setLoading(false);
}
}, [abort]);
// Execute on mount if immediate and enabled
useEffect(() => {
if (immediate && enabled) {
execute();
}
}, [url, immediate, enabled, execute]);
// Re-execute when params change (if immediate and enabled)
useEffect(() => {
// Skip the initial mount - handled above
// This effect handles param changes after mount
}, [params]);
// Cleanup on unmount
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
if (getInstanceRef.current) {
getInstanceRef.current.cancel();
}
};
}, []);
return {
data,
loading,
error,
execute,
abort,
reset,
};
}
//# sourceMappingURL=useGet.js.map