基于@tanstack/react-query v5:useQuery触发useMutation的无限循环问题
解决TanStack React Query v5中useQuery与useMutation的无限循环问题
问题分析
当前代码的无限循环逻辑链:
- useQuery请求用户数据返回404 →
isError为true - 组件渲染时直接调用
mutateAsync创建用户 - mutation成功后调用
invalidateQueries触发useQuery重新请求 - 若useQuery仍返回404(或创建失败),组件重新渲染时再次进入
isError分支,重复调用mutate形成循环 - 组件渲染同步逻辑中直接执行mutate,会导致每次渲染只要
isError为true就触发请求,进一步加剧循环
解决方案
1. 用状态控制避免重复触发创建操作
添加状态标记是否已尝试创建用户,仅在首次遇到404时触发mutation:
function App() { const userInfo = useGetUserQuery(); const createUser = useCreateUserMutation(); const [hasInitiatedCreate, setHasInitiatedCreate] = useState(false); // 成功获取用户后重置状态(可选,根据业务需求调整) useEffect(() => { if (userInfo.isSuccess) { sessionStorage.setItem('user', JSON.stringify(userInfo.data)); setHasInitiatedCreate(false); } }, [userInfo.isSuccess, userInfo.data]); // 仅在404且未尝试过创建时触发mutation useEffect(() => { const attemptCreate = async () => { if (userInfo.isError && !hasInitiatedCreate) { setHasInitiatedCreate(true); try { await createUser.mutateAsync(); } catch (e) { console.error('创建用户失败:', e); setHasInitiatedCreate(false); // 创建失败允许重试 } } }; void attemptCreate(); }, [userInfo.isError, hasInitiatedCreate, createUser]); return ( // 组件内容 ); }
2. 直接更新Query缓存而非重新请求
mutation成功后,直接将创建的用户数据写入Query缓存,无需触发useQuery重新请求,从根源避免循环:
修改useCreateUserMutation的onSuccess逻辑:
export const useCreateUserMutation = () => { const queryClient = useQueryClient(); const auth = useAuth(); const user: NewUser = { // 属性定义 }; return useMutation({ mutationKey: ['user', 'create', user], mutationFn: () => createUser(user), onSuccess: (data) => { // 直接设置对应Query的缓存数据 queryClient.setQueryData( ['user', 'get', data.cnum], data ); }, }); };
此方式会让useQuery立即获取到新创建的用户数据,isSuccess变为true,不再进入错误分支。
3. 区分404错误与其他错误
仅在404时触发创建操作,其他错误(如网络异常)不执行创建,减少不必要的mutation调用:
修改getUser函数的错误处理:
const getUser = async (cnum: string) => { try { const { data } = await api.get<UserInfo>(`/users/get/${cnum}`); return { data, isFound: true }; } catch (error) { // 仅将404视为"未找到",而非错误 if (error.response?.status === 404) { return { data: null, isFound: false }; } // 其他错误抛出,保持isError状态 throw error; } };
然后在App组件中判断:
function App() { const userInfo = useGetUserQuery(); const createUser = useCreateUserMutation(); const [hasInitiatedCreate, setHasInitiatedCreate] = useState(false); useEffect(() => { const attemptCreate = async () => { // 仅在未找到用户(404)且未尝试创建时触发 if (userInfo.data?.isFound === false && !hasInitiatedCreate) { setHasInitiatedCreate(true); try { await createUser.mutateAsync(); } catch (e) { console.error('创建用户失败:', e); setHasInitiatedCreate(false); } } }; void attemptCreate(); }, [userInfo.data, hasInitiatedCreate, createUser]); // 成功获取用户时的逻辑 useEffect(() => { if (userInfo.data?.isFound) { sessionStorage.setItem('user', JSON.stringify(userInfo.data.data)); } }, [userInfo.data]); return ( // 组件内容 ); }
内容的提问来源于stack exchange,提问作者goldenOrder1253
相关产品推荐
相关产品推荐

