React useQuery带variables用onError处理报错及重渲染问题如何解决?
问题根源
- 顶层直接通过
queryColumns.error判断触发弹窗导致死循环的原因:handleErrorDialogOpen内部会调用setState修改组件状态,触发组件重渲染,只要error实例没有被清空,重渲染后会再次执行判断逻辑调用弹窗打开方法,进入无限循环最终崩溃。 - 第一个useQuery的回调不触发的常见原因:
handleErrorDialogOpen、国际化方法t未用useCallback包裹,每次渲染生成新的引用,导致useQuery的配置项每次都是新对象,要么触发重复请求,要么回调被重置- 两个请求并行触发时,后完成的请求的弹窗逻辑会覆盖前一个的弹窗状态,导致第一个请求的弹窗没有展示机会
- React Query v4及以上版本中,内置的
onError/onCompleted默认仅在请求首次完成/失败时触发,如果查询已有缓存数据,回调不会再次执行;v5版本已经将查询级别的onError/onCompleted标记为废弃。
最优解决方案(兼容所有版本,无引用问题)
不用依赖useQuery内置回调,通过useEffect监听请求状态触发弹窗,同时固定所有回调的引用避免重复执行:
// 首先固定弹窗方法的引用 const handleErrorDialogOpen = useCallback((msg) => { // 原有弹窗逻辑不变,建议此处增加错误队列,避免多个错误互相覆盖 setErrorMsg(msg); setIsDialogOpen(true); }, [/* 填入方法内部依赖的所有状态变量 */]); // 国际化方法也固定引用,避免每次渲染生成新实例 const stableT = useCallback(t, []); // useQuery仅保留基础配置,不写回调 const queryColumns = useQuery(web_list_query, { variables: {entity: "device", variant: 'std'}, }); const queryRows = useQuery(getDevices_query, {}); // 单独监听columns的请求状态 useEffect(() => { if (queryColumns.error) { handleErrorDialogOpen(stableT("There was an error retrieving rows data from 'web_list_query: device'")); } if (queryColumns.isSuccess) { handleErrorDialogOpen(stableT("Columns Groovy baby Yeah")); } // 依赖项仅传入状态和固定引用的方法,避免多余触发 }, [queryColumns.error, queryColumns.isSuccess, handleErrorDialogOpen, stableT]); // 单独监听rows的请求状态 useEffect(() => { if (queryRows.error) { handleErrorDialogOpen(stableT("There was an error retrieving rows data from 'getDevices_query: device'")); } if (queryRows.isSuccess) { handleErrorDialogOpen(stableT("Groovy baby ")); } }, [queryRows.error, queryRows.isSuccess, handleErrorDialogOpen, stableT]);
备选方案(仅适用于v3/v4版本)
如果要继续使用内置回调,只需固定配置项里所有方法的引用即可:
const stableHandleError = useCallback(handleErrorDialogOpen, [/* 依赖项 */]); const stableT = useCallback(t, []); const queryColumns = useQuery(web_list_query, { variables: {entity: "device", variant: 'std'}, onError: () => { stableHandleError(stableT("There was an error retrieving rows data from 'web_list_query: device'")); }, onCompleted: ()=> { stableHandleError(stableT("Columns Groovy baby Yeah")); }, // 增加缓存时间避免重复请求触发多次回调 staleTime: 5 * 60 * 1000 }); const queryRows = useQuery(getDevices_query, { onError: () => { stableHandleError(stableT("There was an error retrieving rows data from 'getDevices_query: device'")); }, onCompleted: ()=> { stableHandleError(stableT("Groovy baby ")); }, staleTime: 5 * 60 * 1000 });
内容的提问来源于stack exchange,提问作者Neal Rogers
相关产品推荐
相关产品推荐

