You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Query中queryClient mutations的onError回调无法弹出Toast

Next.js + React-Query 全局Toast不弹出问题解决

核心问题原因

问题出在React Hook的闭包陷阱:useApiError返回的handleApiError里,showToast是组件初始化时捕获的上下文版本,但你通过useState的初始化函数创建QueryClient,这个函数只会执行一次,导致后续mutation触发错误时,handleApiError里的showToast并没有绑定到最新的React上下文,无法触发Toast渲染(但console.log不受上下文影响,所以能正常打印)。

具体修复方案

方案1:用useEffect同步最新的错误处理函数

把QueryClient的创建和配置拆分,用useEffect在组件更新时同步最新的handleApiError,确保每次错误回调都能拿到有效的showToast:

const handleApiError = useApiError();
// 先创建基础的QueryClient
const [queryClient] = React.useState(() => new QueryClient());

React.useEffect(() => {
  // 每次handleApiError更新时,重新设置默认配置
  queryClient.setDefaultOptions({
    queries: {
      retry: 0,
      refetchOnWindowFocus: false,
      throwOnError: true,
      staleTime: 5 * 1000,
    },
    mutations: {
      retry: 0,
      onError: handleApiError,
    },
  });
}, [handleApiError, queryClient]);

方案2:在单个mutation中手动传入onError

放弃全局配置,在每个mutation创建时直接传入最新的handleApiError,绕开闭包问题:

const handleApiError = useApiError();

// 示例mutation
const submitFormMutation = useMutation({
  mutationFn: (data) => yourApiService.submit(data),
  onError: handleApiError,
  // 其他mutation配置
});

方案3:用React-Query全局错误边界捕获

利用React-Query提供的错误边界能力,全局捕获所有query/mutation错误后触发Toast,这种方式更符合React的错误处理逻辑:

// 自定义全局错误边界组件
export function QueryErrorBoundary({ children }) {
  const { showToast } = useToast();
  const { error, reset } = useQueryErrorResetBoundary();

  React.useEffect(() => {
    if (error) {
      const message = ApiError.parsedErrorMessage(error as AxiosError) ?? TOAST_MESSAGE.fail;
      showToast({ type: 'fail', message });
      // 重置错误状态,避免重复触发
      reset();
    }
  }, [error, reset, showToast]);

  return children;
}

// 在_app.tsx中包裹根组件
function MyApp({ Component, pageProps }) {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: 0,
        refetchOnWindowFocus: false,
        throwOnError: true,
        staleTime: 5 * 1000,
      },
      mutations: {
        retry: 0,
      },
    },
  });

  return (
    <QueryClientProvider client={queryClient}>
      <QueryErrorBoundary>
        <Component {...pageProps} />
      </QueryErrorBoundary>
    </QueryClientProvider>
  );
}

关键注意事项

  • 不要在useState的初始化函数中依赖外部Hook的返回值,因为这个函数只会执行一次,无法响应后续的上下文或状态变化。
  • 如果坚持用全局配置,必须确保配置能同步最新的Hook值,useEffect是最可靠的方式。

内容的提问来源于stack exchange,提问作者2ne1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:05:25