React-query循环调用useMutation仅能获取最后请求ID问题咨询
问题解答:遍历数组调用mutate仅获取最后一次请求的id
你的猜测完全正确——确实是因为onSuccess回调被覆盖(或者说异步回调的执行时机与共享变量的特性导致的),下面来详细拆解原因和对应的解决方案:
核心原因分析
先看你的代码逻辑:
array?.forEach((item, index) => { mutate( { ...item }, { onSuccess: ({ id }) => { console.log(id) }, } ); });
情况1:自定义mutate的封装缺陷
如果你的mutate是自己封装的函数,且内部用了共享变量存储onSuccess回调(比如下面这种错误写法):
// 错误封装的mutate示例 let currentOnSuccess; function mutate(data, options) { currentOnSuccess = options?.onSuccess; // 发起网络请求 fetch('/your-api', { method: 'POST', body: JSON.stringify(data) }) .then(res => res.json()) .then(result => { // 请求完成后调用共享的回调变量 if (currentOnSuccess) currentOnSuccess(result); }); }
forEach是同步遍历,会快速执行完所有mutate调用,每次调用都会把currentOnSuccess替换成当前循环的onSuccess函数。当所有异步请求陆续完成时,currentOnSuccess已经是最后一次循环赋值的回调,所以所有请求都会触发最后一次的onSuccess,自然只能拿到最后一个id。
情况2:状态管理库的特殊配置(如React Query)
如果你用的是React Query这类库的useMutation:
- 默认情况下,每次
mutate的onSuccess是独立的,不会被覆盖,但如果开启了cancelPrevious: true选项,后续的mutate会取消之前的请求,导致只有最后一次请求能完成; - 另外如果你的
onSuccess错误引用了循环中被覆盖的变量(比如没通过闭包捕获循环变量),也会出现类似问题,但你的代码直接用返回的id,这种情况概率较低。
解决方案
方案1:修复自定义mutate的封装
确保每个mutate调用的回调是独立绑定的,不要用共享变量存储,直接在请求的then链中使用当前的options.onSuccess:
// 正确封装的mutate示例 function mutate(data, options) { const currentOnSuccess = options?.onSuccess; fetch('/your-api', { method: 'POST', body: JSON.stringify(data) }) .then(res => res.json()) .then(result => { if (currentOnSuccess) currentOnSuccess(result); }); }
这样每个请求的回调都会被单独保存,不会被后续调用覆盖。
方案2:确保状态管理库中请求的独立性(以React Query为例)
如果用React Query,坚持用useMutation的话,确保关闭cancelPrevious(默认就是false),或者用useQueries批量处理数组中的请求,更清晰地跟踪每个请求的状态:
// 使用useQueries批量处理每个item的请求 const queries = useQueries( array?.map(item => ({ queryKey: ['mutate-item', item.id], // 给每个请求设置唯一标识 queryFn: () => fetch('/your-api', { method: 'POST', body: JSON.stringify(item) }).then(res => res.json()), enabled: false, // 手动触发请求 onSuccess: ({ id }) => { console.log(id); } })) || [] ); // 触发所有请求 const triggerAllMutations = () => { queries.forEach(query => query.refetch()); };
方案3:闭包捕获独立回调(临时应急方案)
如果暂时无法修改mutate的封装,可以用立即执行函数(IIFE)创建闭包,在循环中捕获每个独立的onSuccess:
array?.forEach((item) => { // 用IIFE创建闭包,锁定当前的onSuccess (function(successCallback) { mutate( { ...item }, { onSuccess: successCallback } ); })(({ id }) => { console.log(id); }); });
不过这只是临时 workaround,更推荐从根源修复mutate的封装逻辑。
内容的提问来源于stack exchange,提问作者ImpsImperial
相关产品推荐
相关产品推荐

