使用useSWR钩子执行GraphQL Mutation查询时变量传递错误的排查求助
看起来你遇到的核心问题是没有正确把变量传递到GraphQL Mutation请求中,而且对useSWR处理Mutation的方式存在一些误解。让我一步步帮你理清问题并解决:
问题根源
- useSWR的默认逻辑不适合Mutation:useSWR本质是为数据获取(GET类操作)设计的,默认的fetcher函数不会处理GraphQL Mutation需要的POST请求格式,也不会正确携带变量。
- mutate调用方式错误:你传递给
mutate的参数没有被解析为GraphQL请求所需的变量结构,导致后端提示$data变量未提供。
解决方案
第一步:创建自定义GraphQL Fetcher函数
首先需要一个专门处理GraphQL查询和Mutation的fetcher,它要包含POST请求、正确的请求体格式,以及MongoDB Realm要求的认证信息。
在项目中新增一个工具文件(比如utils/graphqlFetcher.ts):
import { useContext } from "react"; import { AuthContext } from "../context/AuthContext"; export async function graphqlFetcher(query: string, variables = {}) { // 替换成你的MongoDB Atlas Realm GraphQL端点 const API_URL = "https://realm.mongodb.com/api/client/v2.0/app/[你的APP ID]/graphql"; // 从AuthContext获取用户的access token(根据你的Context结构调整) const user = useContext(AuthContext); const token = user?.accessToken; const res = await fetch(API_URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, // Realm认证必需的请求头 }, body: JSON.stringify({ query, variables, // 这里将变量正确传递给GraphQL服务 }), }); const data = await res.json(); if (data.errors) { throw new Error(data.errors[0].message); } return data; }
第二步:修正Mutation的执行方式
useSWR本身更适合数据查询,对于写操作,推荐使用SWR 2.x提供的useSWRMutation钩子(专门处理Mutation),这会让代码更清晰:
方案1:使用useSWRMutation(推荐)
先确保安装了最新版SWR,然后修改你的组件:
// 导入useSWRMutation import useSWRMutation from 'swr/mutation'; import { graphqlFetcher } from '../utils/graphqlFetcher'; // 替换原来的useSWR(INSERT_TRANSACTION) const { trigger, isMutating } = useSWRMutation( 'insert-transaction', // 自定义缓存key,用于标识这个Mutation (_, { arg }) => graphqlFetcher(INSERT_TRANSACTION, arg) ); // 在onFinish里调用trigger执行Mutation const onFinish = async (values) => { const txnData = { date: new Date(), user_id: userId, category: values.category[0], subcategory: values.category[1], type: values.txntype, amount: values.amount, description: values.description, mode: values.mode, account: { account_name: "ICICI bank", bank_name: "ICICI bank", type: "savings", }, }; try { await trigger({ data: txnData }); console.log("Transaction inserted successfully!"); setVisible(false); // 如果需要刷新其他查询数据,比如交易列表,可以调用对应useSWR的mutate // 示例:mutate(TRANSACTION_LIST_QUERY); } catch (err) { console.error("Error inserting transaction:", err); } };
方案2:直接调用Fetcher(适合老版本SWR)
如果你不想用useSWRMutation,也可以直接在onFinish里调用自定义fetcher,无需借助useSWR的缓存逻辑:
const onFinish = async (values) => { const txnData = { /* 构造数据 */ }; try { await graphqlFetcher(INSERT_TRANSACTION, { data: txnData }); console.log("Transaction inserted successfully!"); setVisible(false); // 刷新相关数据 } catch (err) { console.error("Error inserting transaction:", err); } };
第三步:确认认证信息有效性
确保你的AuthContext中包含了用户的accessToken,MongoDB Realm的GraphQL API需要这个令牌来验证用户权限,否则Mutation会被拒绝。如果你的Context只存储了userId,需要调整为存储完整的用户对象(包含accessToken)。
为什么你的代码会报错?
你之前的代码中,useSWR(INSERT_TRANSACTION)只是把Mutation字符串作为缓存key,默认的fetcher会尝试用GET请求去获取这个字符串对应的资源,完全不符合GraphQL Mutation的要求。而调用mutate([INSERT_TRANSACTION, { data: txnData }])时,参数没有被解析为GraphQL需要的变量结构,导致后端接收不到$data变量。
内容的提问来源于stack exchange,提问作者vishalshettydev

