UNPKG

@knowmax/genericlist-core

Version:

Knowmax Generic list with basic CRUD support without any user interface implementation.

141 lines (140 loc) 5.72 kB
import { useEffect, useMemo, useState, useCallback } from "react"; import { headers, FetchError } from "@knowmax/http-utils"; /** * A simplified React hook for fetching paginated list data from an API endpoint. * * This hook is designed as a lightweight alternative to the more complex `useList` hook, * providing essential list functionality without advanced features like caching, * state management, or CRUD operations. * * **Key Features:** * - Simple pagination support * - Automatic request abortion on unmount/dependency changes * - Built-in loading and error states * - TypeScript support with generic type parameter * - Manual refetch capability * * **Use Cases:** * - Read-only data lists * - Simple pagination scenarios * - When you don't need advanced caching or state management * - Lightweight components with minimal list requirements * * @template T - The type of objects in the list * @param configuration - Configuration object containing endpoint, authentication, and query parameters * @param dependencies - Optional React dependencies array to trigger refetch when values change * @returns An object containing the list result, loading state, error state, and refetch function * * @example * ```tsx * // Basic usage * const { result, isLoading, error, refetch } = useSimpleList<User>({ * endpoint: '/api/users', * token: 'your-auth-token', * pageSize: 20, * page: 1 * }); * * // With filtering and ordering * const { result, isLoading, error } = useSimpleList<Product>({ * endpoint: '/api/products', * token: authToken, * filter: 'category eq "electronics"', * order: 'name ASC', * count: true * }); * * // With dependencies * const [searchTerm, setSearchTerm] = useState(''); * const { result, isLoading } = useSimpleList<Article>({ * endpoint: '/api/articles', * token: authToken, * filter: searchTerm ? `title contains "${searchTerm}"` : undefined * }, [searchTerm]); * ``` * * @see {@link useList} for the full-featured alternative with caching and CRUD operations */ export const useSimpleList = (configuration, dependencies) => { const [result, setResult] = useState(undefined); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(undefined); const [status, setStatus] = useState(undefined); /** * Internal function to fetch data from the API endpoint. * Handles request building, authentication, error handling, and abort signals. * * @param abortSignal - Optional AbortSignal to cancel the request */ const fetchData = useCallback(async (abortSignal) => { if (configuration.token) { try { setIsLoading(true); setError(undefined); // Build request body with all parameters using strongly typed IListRequest const requestBody = { take: configuration.pageSize ?? 10, skip: ((configuration.page ?? 1) - 1) * (configuration.pageSize ?? 10), orderBy: configuration.order || '', filter: configuration.filter, count: configuration.count ?? false }; // Build headers with optional bearer token and JSON content type const requestHeaders = headers().withContentTypeJson().withBearer(configuration.token); const response = await fetch(configuration.endpoint, { method: 'post', headers: requestHeaders.export(), body: JSON.stringify(requestBody), signal: abortSignal }); setStatus(response.status); if (!response.ok) { const errorData = await response.json().catch(() => undefined); throw new FetchError(response, errorData); } const fetchedResult = await response.json(); // Only update state if the request wasn't aborted if (!abortSignal?.aborted) { setResult(fetchedResult); } } catch (err) { // Don't set error state if the request was aborted if (err instanceof Error && err.name === 'AbortError') { return; } const errorInstance = err instanceof Error ? err : new Error('An unknown error occurred'); if (!abortSignal?.aborted) { setError(errorInstance); setResult(undefined); } } finally { if (!abortSignal?.aborted) { setIsLoading(false); } } } }, [configuration.endpoint, configuration.token, configuration.order, configuration.filter, configuration.page, configuration.pageSize]); /** * Public function to manually trigger a data refetch. * Useful for refreshing data after external changes or user actions. */ const refetch = useCallback(async () => { await fetchData(); }, [fetchData]); useEffect(() => { const abortController = new AbortController(); fetchData(abortController.signal); // Cleanup function to abort the request if component unmounts or dependencies change return () => { abortController.abort(); }; }, dependencies ? [fetchData, configuration.token, ...dependencies] : [fetchData]); return useMemo(() => ({ result, isLoading, error, refetch }), [result, isLoading, error, refetch]); };