UNPKG

rnr-starter

Version:

A comprehensive React Native Expo boilerplate with 50+ modern UI components, dark/light themes, i18n, state management, and production-ready architecture

231 lines (190 loc) 5.2 kB
import axios, { type AxiosError, type AxiosResponse } from 'axios'; import { toast } from '~/lib/stores/toast-store'; /** * Configuration for error interceptor */ export interface ErrorInterceptorConfig { enabled: boolean; showNetworkErrors: boolean; showServerErrors: boolean; showClientErrors: boolean; ignoredStatuses: number[]; customErrorMap: Record<number, string>; prefix: string; } const defaultConfig: ErrorInterceptorConfig = { enabled: true, showNetworkErrors: true, showServerErrors: true, showClientErrors: true, ignoredStatuses: [401], // Often handled separately for auth customErrorMap: {}, prefix: 'api.errors', }; let config = { ...defaultConfig }; /** * Response interceptor for handling API errors */ const responseInterceptor = (response: AxiosResponse): AxiosResponse => response; /** * Handle network errors */ const handleNetworkError = (error: AxiosError): boolean => { if (!error.response && config.showNetworkErrors) { toast.errorI18n(`${config.prefix}.network`); return true; } return false; }; /** * Check if status should be ignored */ const shouldIgnoreStatus = (status: number): boolean => { return config.ignoredStatuses.includes(status); }; /** * Check if error type should be shown */ const shouldShowError = (status: number): boolean => { const isClientError = status >= 400 && status < 500; const isServerError = status >= 500; if (isClientError && !config.showClientErrors) { return false; } if (isServerError && !config.showServerErrors) { return false; } return true; }; /** * Get error message key for status */ const getErrorKey = (status: number): string => { // Use custom error message if available if (config.customErrorMap[status]) { return config.customErrorMap[status]; } // Default error message based on status let errorKey = `${config.prefix}.${status}`; // Fallback to generic messages for unmapped statuses const isClientError = status >= 400 && status < 500; const isServerError = status >= 500; if (isClientError) { errorKey = `${config.prefix}.client`; } else if (isServerError) { errorKey = `${config.prefix}.server`; } return errorKey; }; /** * Handle response errors */ const handleResponseError = (error: AxiosError): void => { if (!error.response) return; const status = error.response.status; if (shouldIgnoreStatus(status) || !shouldShowError(status)) { return; } const errorKey = getErrorKey(status); toast.errorI18n(errorKey); }; /** * Error interceptor for handling API errors */ const errorInterceptor = (error: AxiosError): Promise<AxiosError> => { if (!config.enabled) { return Promise.reject(error); } // Handle network errors if (handleNetworkError(error)) { return Promise.reject(error); } // Handle response errors handleResponseError(error); return Promise.reject(error); }; /** * Setup error interceptor for axios */ export const setupErrorInterceptor = ( axiosInstance = axios, customConfig?: Partial<ErrorInterceptorConfig> ) => { // Update config if (customConfig) { config = { ...config, ...customConfig }; } // Add interceptors const responseInterceptorId = axiosInstance.interceptors.response.use( responseInterceptor, errorInterceptor ); // Return cleanup function return () => { axiosInstance.interceptors.response.eject(responseInterceptorId); }; }; /** * Configure error interceptor */ export const configureErrorInterceptor = (newConfig: Partial<ErrorInterceptorConfig>) => { config = { ...config, ...newConfig }; }; /** * Enable/disable error interceptor */ export const toggleErrorInterceptor = (enabled: boolean) => { config.enabled = enabled; }; /** * Get current configuration */ export const getErrorInterceptorConfig = (): ErrorInterceptorConfig => ({ ...config }); /** * Reset configuration to defaults */ export const resetErrorInterceptorConfig = () => { config = { ...defaultConfig }; }; /** * Manually handle error with toast (for custom error handling) */ export const handleErrorWithToast = (error: any, context?: string, customMessage?: string) => { if (customMessage) { toast.error(customMessage); return; } let errorKey = `${config.prefix}.unknown`; if (error?.response?.status) { errorKey = `${config.prefix}.${error.response.status}`; } else if (!error.response) { errorKey = `${config.prefix}.network`; } if (context) { errorKey = `${context}.${errorKey}`; } toast.errorI18n(errorKey); }; // Auto-setup for default axios instance let defaultCleanup: (() => void) | null = null; /** * Initialize error interceptor with default settings */ export const initializeErrorInterceptor = (customConfig?: Partial<ErrorInterceptorConfig>) => { // Cleanup previous setup if (defaultCleanup) { defaultCleanup(); } // Setup new interceptor defaultCleanup = setupErrorInterceptor(axios, customConfig); return defaultCleanup; }; /** * Cleanup default error interceptor */ export const cleanupErrorInterceptor = () => { if (defaultCleanup) { defaultCleanup(); defaultCleanup = null; } };