'use client'; import { ApiError } from '@/types/api'; import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import React, { useState, useRef, type ReactNode } from 'react'; import { toast, Toaster } from 'sonner'; import { tokenManager } from './auth/token'; import { COIN_INSUFFICIENT_ERROR_CODE } from '@/hooks/useWallet'; import { walletKeys } from './query-keys'; interface ProvidersProps { children: ReactNode; } // const ReactQueryDevtoolsProduction = React.lazy(() => // import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({ // default: d.ReactQueryDevtools, // })) // ); const EXPIRED_ERROR_CODES = [ '10050001', '10050002', '10050003', '10050004', '10050005', '10050006', ]; export function Providers({ children }: ProvidersProps) { const router = useRouter(); // 用于错误去重的引用 const errorTimeoutRef = useRef(null); const lastErrorRef = useRef(''); const [queryClient] = useState( () => new QueryClient({ defaultOptions: { queries: { // 全局查询配置 staleTime: 5 * 60 * 1000, // 5分钟后数据过期 gcTime: 10 * 60 * 1000, // 10分钟后清除缓存 refetchOnWindowFocus: false, // 窗口聚焦时不重新请求 retry: 0, // 失败重试次数 }, mutations: { // 全局修改配置 retry: 0, }, }, queryCache: new QueryCache({ onError: (error) => { handleError(error as ApiError); }, }), mutationCache: new MutationCache({ onError: (error) => { handleError(error as ApiError); }, }), }) ); const pathname = usePathname(); const searchParams = useSearchParams(); const searchParamsString = searchParams.toString(); const redirectURL = `${pathname}${searchParamsString ? `?${searchParamsString}` : ''}`; const handleError = (error: ApiError) => { if (error.errorCode === COIN_INSUFFICIENT_ERROR_CODE) { queryClient.invalidateQueries({ queryKey: walletKeys.getWalletBalance() }); } if (EXPIRED_ERROR_CODES.includes(error.errorCode)) { // TODO: 清除 cookie 中的 st // tokenManager.removeToken(); // router.push('/login?redirect=' + encodeURIComponent(redirectURL)); return; // 对于登录过期错误,不显示错误toast,直接跳转 } if (error.ignoreError) { return; } // 错误去重逻辑:只显示最后一次的错误 const errorKey = `${error.errorCode}:${error.errorMsg}`; // 清除之前的定时器 if (errorTimeoutRef.current) { clearTimeout(errorTimeoutRef.current); } // 更新最后一次错误信息 lastErrorRef.current = errorKey; // 设置新的定时器,延迟显示错误 errorTimeoutRef.current = setTimeout(() => { // 只有当前错误仍然是最后一次错误时才显示 if (lastErrorRef.current === errorKey) { toast.error(error.errorMsg); } }, 100); // 100ms 延迟,确保能捕获到快速连续的错误 }; return ( {children} , success: , }} toastOptions={{ className: '!bg-surface-base-normal !border-none !px-4 !py-3 !rounded-m !txt-body-m !text-txt-primary-normal', }} /> {/* */} {/* {showDevtools && ( )} */} ); }