UNPKG

react-antd-admin-panel

Version:

Modern TypeScript-first React admin panel builder with Ant Design 6

146 lines 4.38 kB
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