feat: Implement typed error contracts in generic hooks
Introduce discriminated FetchError union type to replace weak string error handling in API calls and hooks. Enables actionable error diagnostics. Changes: - Create types/api.ts with FetchError discriminated union (api_error, network_error, abort_error) - Export type guards: isAuthError, isAbortError, isNetworkError, isApiError - Update useListData and usePolledData to expose typed FetchError instead of string - Add getErrorMessage() helper to extract displayable messages from FetchError - Add createStringErrorAdapter() for backward compatibility with string error state - Update handleFetchError() to work with both FetchError and string setters - Update all consumer hooks to expose typed errors - Update components to use getErrorMessage() when displaying errors - Update tests to mock FetchError instead of strings - Add comprehensive typed error model documentation to Web-Development.md This enables better error handling patterns: - Check error.type to distinguish between API, network, and abort errors - Extract status codes for specific handling (401/403 auth, 50x server errors) - Maintain backward compatibility with existing string-based error states All TypeScript compilation passes with no errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { handleFetchError } from "../utils/fetchError";
|
||||
import type { FetchError } from "../types/api";
|
||||
|
||||
export interface UsePolledDataOptions<TResponse, TData> {
|
||||
fetcher: (signal: AbortSignal) => Promise<TResponse>;
|
||||
@@ -20,15 +21,23 @@ export interface UsePolledDataOptions<TResponse, TData> {
|
||||
export interface UsePolledDataResult<TData> {
|
||||
data: TData | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
error: FetchError | null;
|
||||
refresh: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single-item response and expose refresh semantics with polling support.
|
||||
*
|
||||
* Provides typed error handling through the `error` property, which is either
|
||||
* `null` or a discriminated `FetchError` union. Use the error's `type` field
|
||||
* to determine how to handle it:
|
||||
*
|
||||
* - `"api_error"`: Server returned HTTP error (check `status` for 401/403/50x)
|
||||
* - `"network_error"`: Network, DNS, or JSON parse failure
|
||||
* - `"abort_error"`: Request was cancelled (typically silently ignored by hook)
|
||||
*
|
||||
* @param options - Configuration options
|
||||
* @returns Data, loading state, error, and refresh callback
|
||||
* @returns Data, loading state, typed error, and refresh callback
|
||||
*/
|
||||
export function usePolledData<TResponse, TData>(
|
||||
options: UsePolledDataOptions<TResponse, TData>,
|
||||
@@ -45,7 +54,7 @@ export function usePolledData<TResponse, TData>(
|
||||
|
||||
const [data, setData] = useState<TData | null>(initialData ?? null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [error, setError] = useState<FetchError | null>(null);
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
const fetchRef = useRef<() => void>((): void => undefined);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user