@orchard9ai/error-handling
Version:
Federated error handling package with go-core-http-toolkit format support and logging integration
109 lines (91 loc) • 2.86 kB
text/typescript
import * as React from 'react';
import { HttpClient, type HttpClientConfig } from '../handlers/HttpClient.js';
/**
* React hook for HTTP client with automatic cleanup
*/
export function useHttpClient(config?: HttpClientConfig): HttpClient {
const abortControllers = React.useRef<Set<AbortController>>(new Set());
// Create client once
const client = React.useMemo(() => {
const httpClient = new HttpClient(config);
// Wrap request method to track abort controllers
const originalRequest = httpClient.request.bind(httpClient);
httpClient.request = async function(endpoint, requestConfig = {}) {
const controller = new AbortController();
abortControllers.current.add(controller);
try {
const result = await originalRequest(endpoint, {
...requestConfig,
signal: controller.signal
});
abortControllers.current.delete(controller);
return result;
} catch (error) {
abortControllers.current.delete(controller);
throw error;
}
};
return httpClient;
}, []); // Only create once
// Cleanup on unmount
React.useEffect(() => {
return () => {
// Abort all pending requests
abortControllers.current.forEach(controller => {
controller.abort();
});
abortControllers.current.clear();
};
}, []);
return client;
}
/**
* Hook for making HTTP requests with automatic cleanup
*/
export function useHttpRequest<T = any>(
client: HttpClient,
endpoint: string,
config?: Parameters<HttpClient['request']>[1]
) {
const [state, setState] = React.useState<{
data: T | null;
loading: boolean;
error: Error | null;
}>({
data: null,
loading: false,
error: null
});
const abortControllerRef = React.useRef<AbortController | null>(null);
const execute = React.useCallback(async () => {
// Abort previous request
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
const controller = new AbortController();
abortControllerRef.current = controller;
setState({ data: null, loading: true, error: null });
try {
const response = await client.request<T>(endpoint, {
...config,
signal: controller.signal
});
if (!controller.signal.aborted) {
setState({ data: response.data, loading: false, error: null });
}
} catch (error) {
if (!controller.signal.aborted) {
setState({ data: null, loading: false, error: error as Error });
}
}
}, [client, endpoint, config]);
// Cleanup on unmount
React.useEffect(() => {
return () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
};
}, []);
return { ...state, execute };
}