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

Next.js 13中GraphQL Mutation执行异常排查求助

问题:Next.js 13中GraphQL Mutation无法执行(Query正常,表单位于Modal内)

所有Query均可正常运行,但GraphQL Mutation完全无法执行。相同代码在其他项目中可正常使用,唯一差异是当前表单位于Modal组件中。该Mutation在GraphQL Playground内可正常执行。

注册组件代码

"use client";
import React, { useContext } from "react";
import { gql, useMutation } from "@apollo/client";
import { useForm, SubmitHandler } from "react-hook-form";
import Modal from "../Modal/Modal";
import { ModalContext } from "@/app/contex/modal";

// 定义GraphQL mutation
const REGISTER_USER_MUTATION = gql`
  mutation RegisterUser($input: RegisterInput!) {
    register(input: $input) {
      jwt
      user {
        id
        username
        email
        confirmed
        role {
          id
        }
      }
    }
  }
`;

interface RegistrationFormData {
  username: string;
  email: string;
  password: string;
}

const Register: React.FC = () => {
  const modalContext = useContext(ModalContext);

  // 确保modalContext可用
  if (!modalContext) {
    throw new Error("ModalContext not available");
  }

  const { modalStatus, setModalStatus } = modalContext;
  const {
    register,
    handleSubmit,
    reset,
    formState: { errors },
    watch,
  } = useForm<RegistrationFormData>();

  // 定义useMutation钩子
  const [registerUser, { loading, error, data }] = useMutation<{
    register: { jwt: string; user: User };
  }>(REGISTER_USER_MUTATION);

  const onSubmit: SubmitHandler<RegistrationFormData> = async (data) => {
    try {
      const response = await registerUser({
        variables: {
          input: {
            username: data.username,
            email: data.email,
            password: data.password,
          },
        },
      });

      if (response.errors) {
        console.error("GraphQL errors:", response.errors);
        return;
      }

      console.log("用户注册响应:", response.data);
      reset();
    } catch (err) {
      console.error("注册错误:", err);
    }
  };

  const password = watch("password");

  return (
    <Modal>
      <h1>注册</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label htmlFor="username">用户名</label>
          <input
            type="text"
            id="username"
            {...register("username", { required: "用户名不能为空" })}
          />
          {errors.username && <span>{errors.username.message}</span>}
        </div>
        <div>
          <label htmlFor="email">邮箱</label>
          <input
            type="email"
            id="email"
            {...register("email", { required: "邮箱不能为空" })}
          />
          {errors.email && <span>{errors.email.message}</span>}
        </div>
        <div>
          <label htmlFor="password">密码</label>
          <input
            type="password"
            id="password"
            {...register("password", { required: "密码不能为空" })}
          />
          {errors.password && <span>{errors.password.message}</span>}
        </div>
        <div>
          <label htmlFor="confirmPassword">确认密码</label>
          <input
            type="password"
            id="confirmPassword"
            {...register("confirmPassword", {
              required: "请确认密码",
              validate: (value) =>
                value === password || "密码不匹配",
            })}
          />
          {errors.confirmPassword && (
            <span>{errors.confirmPassword.message}</span>
          )}
        </div>
        <div>
          <button type="submit">注册</button>
        </div>
      </form>
      <button
        onClick={() => {
          setModalStatus("");
        }}
      >
        关闭
      </button>
    </Modal>
  );
};

export default Register;

lib/apollo-wrapper.js代码

"use client";

import {
  ApolloClient,
  ApolloLink,
  HttpLink,
} from "@apollo/client";
import {
  ApolloNextAppProvider,
  NextSSRInMemoryCache,
  SSRMultipartLink, 
  NextSSRApolloClient
} from "@apollo/experimental-nextjs-app-support/ssr";

function makeClient() {
  const httpLink = new HttpLink({
      uri: process.env.NEXT_PUBLIC_API_ENDPOINT,
  });

  return new NextSSRApolloClient({
    cache: new NextSSRInMemoryCache(),
    link:
      typeof window === "undefined"
        ? ApolloLink.from([
            new SSRMultipartLink({
              stripDefer: true,
            }),
            httpLink,
          ])
        : httpLink,
  });
}

export function ApolloWrapper({ children }) {
  return (
    <ApolloNextAppProvider makeClient={makeClient}>
      {children}
    </ApolloNextAppProvider>
  );
}

lib/client.js代码

import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import {
  NextSSRInMemoryCache,
  NextSSRApolloClient,
} from "@apollo/experimental-nextjs-app-support/ssr";
import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc";

export const { getClient } = registerApolloClient(() => {
  return new NextSSRApolloClient({
    cache: new NextSSRInMemoryCache(),
    link: new HttpLink({
      uri: process.env.NEXT_PUBLIC_API_ENDPOINT,
    }),
  });
});

排查与解决方案

1. 检查Modal组件的事件拦截

很多自定义Modal会默认阻止事件冒泡或表单默认提交行为,导致onSubmit无法触发:

  • 在onSubmit开头添加console.log("表单提交触发"),确认函数是否被调用
  • 检查Modal组件的根元素,是否存在onClick事件中调用e.preventDefault()或e.stopPropagation(),如果有,修改逻辑避免影响表单提交

2. 修复React Hook Form的类型定义错误

你的RegistrationFormData接口未包含confirmPassword字段,但表单中使用了该字段,会导致类型校验失败,阻止表单提交。修改接口:

interface RegistrationFormData {
  username: string;
  email: string;
  password: string;
  confirmPassword: string; // 添加该字段
}

3. 确认Apollo上下文覆盖范围

确保ApolloWrapper包裹了整个应用(包括Modal的渲染层级),否则组件无法获取Apollo客户端实例。可以在Register组件内添加以下代码验证:

import { useApolloClient } from '@apollo/client';

// 组件内部
const client = useApolloClient();
console.log("Apollo客户端是否可用:", !!client);

如果输出false,需要调整ApolloWrapper的位置,确保Modal在其覆盖范围内。

4. 排查Apollo配置冲突

你同时维护了apollo-wrapper.js和client.js两个配置,确认:

  • 应用根布局(如app/layout.js)仅使用ApolloWrapper包裹,未重复引入其他Apollo客户端
  • 浏览器开发者工具的Network标签中,查看Mutation请求是否发送,是否存在跨域错误或4xx/5xx状态码

5. 完善错误日志

在onSubmit中添加更详细的日志,定位问题:

const onSubmit: SubmitHandler<RegistrationFormData> = async (data) => {
  console.log("提交的表单数据:", data);
  console.log("开始调用Mutation");
  try {
    const response = await registerUser({
      variables: {
        input: {
          username: data.username,
          email: data.email,
          password: data.password,
        },
      },
    });
    console.log("Mutation响应:", response);
    // 原有逻辑
  } catch (err) {
    console.error("完整错误信息:", JSON.stringify(err, null, 2));
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:35:54