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

React中发送GraphQL Mutation时setTimeout相关问题及替代方案

GraphQL useMutation 同步调用被取消问题分析与解决

问题背景

需要将单个表单的值拆分为两部分,通过GraphQL的calc mutation发送到同一接口。使用React的useMutation Hook实现时发现:通过setTimeout延迟调用第二个mutation,两个请求均能成功;但同步调用时,第二个请求会处于取消状态且无响应。


相关代码

GraphQL Mutation

mutation calc($applicationRecipientsFilter: ApplicationRecipientsFilterInput) {    
    create(applicationRecipientsFilter: $applicationRecipientsFilter) {
        id
        city {
            id
            name    
        }
        applicationType    
        recipients      
    }
}

Mutation Hook封装

const { mutate } = useMutation(calc);

const addNewNotification = useCallback( 
    async (args) => { 
        try { 
            const { error } = await mutate(args); 
            if (error) { 
                console.log(error)
                return; 
            } 
        } catch (e) { 
            console.log(e)
        } 
    }, 
    [mutate], 
);

组件调用逻辑

// 延迟调用(执行成功)
const handleAddNotification = useCallback(() => { 
    const { city, email, email2 } = values; // 来自useFormik的表单值
    const currentCity = citiesOption.find(({ value }) => value === city); 

    const createArguments = (applicationType, recipients) => ({ 
        applicationRecipients: { 
            city: { 
                name: currentCity?.label, 
                id: currentCity?.value, 
            }, 
            applicationType, 
            recipients, 
        }, 
    }); 

    addNewNotification(createArguments(creditTypeOption[0]?.value, email)); 

    setTimeout(() => addNewNotification(createArguments(creditTypeOption[1]?.value, email2)), 1500); 
}, [addNewNotification, creditTypeOption, values, citiesOption]);

// 同步调用(请求被取消)
// addNewNotification(createArguments(creditTypeOption[0]?.value, email)); 
// addNewNotification(createArguments(creditTypeOption[1]?.value, email2)); 

请求状态截图:

  • 取消状态:请求取消状态
  • 成功状态:成功请求状态

问题原因

Apollo Client的useMutation钩子返回的mutate函数,默认会自动取消之前尚未完成的同mutation请求。这是Apollo内置的竞态处理机制,用于避免旧请求结果覆盖新请求,但在需要并行发送同mutation的场景下,该机制会导致先发起的请求被后一个请求直接取消。

使用setTimeout延迟调用时,第一个请求已完成,第二个请求不会触发取消逻辑,因此能正常执行。


不使用setTimeout的解决方案

方案1:调用mutate时禁用取消未完成请求

在调用mutate时通过context设置cancelRefetch: false,允许同mutation的多个请求并行执行,代码改动最小:

const addNewNotification = useCallback( 
    async (args) => { 
        try { 
            const { error } = await mutate(args, {
                context: {
                    cancelRefetch: false // 禁用同mutation请求的自动取消
                }
            }); 
            if (error) { 
                console.log(error)
                return; 
            } 
        } catch (e) { 
            console.log(e)
        } 
    }, 
    [mutate], 
);

之后可直接同步调用,或用Promise.all并行执行:

const handleAddNotification = useCallback(async () => { 
    // 省略createArguments等逻辑
    await Promise.all([
        addNewNotification(createArguments(creditTypeOption[0]?.value, email)),
        addNewNotification(createArguments(creditTypeOption[1]?.value, email2))
    ]);
}, [addNewNotification, creditTypeOption, values, citiesOption]);

方案2:创建两个独立的useMutation实例

如果需要频繁并行发送该mutation,可直接创建两个独立的useMutation钩子,它们的请求不会互相干扰:

const { mutate: mutate1 } = useMutation(calc);
const { mutate: mutate2 } = useMutation(calc);

const addNewNotification1 = useCallback( 
    async (args) => { 
        try { 
            const { error } = await mutate1(args); 
            if (error) console.log(error);
        } catch (e) { console.log(e) }
    }, 
    [mutate1], 
);

const addNewNotification2 = useCallback( 
    async (args) => { 
        try { 
            const { error } = await mutate2(args); 
            if (error) console.log(error);
        } catch (e) { console.log(e) }
    }, 
    [mutate2], 
);

// 组件调用
const handleAddNotification = useCallback(() => { 
    // 省略createArguments等逻辑
    addNewNotification1(createArguments(creditTypeOption[0]?.value, email));
    addNewNotification2(createArguments(creditTypeOption[1]?.value, email2));
}, [addNewNotification1, addNewNotification2, creditTypeOption, values, citiesOption]);

方案3:修改mutation的fetchPolicy

结合fetchPolicy: 'no-cache'使用(适合不需要缓存的场景),同时配合cancelRefetch: false效果更好:

const { mutate } = useMutation(calc, {
    fetchPolicy: 'no-cache'
});

总结

最推荐方案1,通过cancelRefetch: false灵活控制请求并行逻辑,无需额外创建多个mutation实例,代码侵入性最低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 08:17:44