You need to enable JavaScript to run this app. React.js表单提交触发GraphQL Mutation时useMutation规则报错咨询
优惠活动
大模型
产品
解决方案
定价
更多

React.js表单提交触发GraphQL Mutation时useMutation规则报错咨询

报错根因

触发react-hooks/rules-of-hooks校验的核心原因是违反了React Hooks的强制使用规则:

  • 所有Hook必须在React函数组件、自定义Hook的顶层作用域调用,禁止在事件回调、条件判断分支、普通业务函数内部初始化Hook。
  • 你之前的实现将useMutation的初始化逻辑写在了表单onSubmit回调内部,相当于在普通的事件处理器中调用Hook,自然会被ESLint规则拦截。
合规实现方案

按照React Hooks规则调整结构后,即可实现表单提交触发GraphQL Mutation的需求,调整后的核心逻辑完全匹配你使用的react-bootstrap、react-hook-form、@apollo/client、react-router-dom技术栈:

  1. 将GraphQL Mutation语句、useMutation初始化调用移动到Register组件的顶层作用域,拿到暴露的请求触发函数。
  2. 在onSubmit回调中,直接调用顶层拿到的请求触发函数,传入表单收集的参数即可。

完整实现示例:

import { Form, Button } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { useMutation, gql } from '@apollo/client';

// 组件外部定义Mutation查询语句,避免组件重渲染时重复创建文档节点
const REGISTER_USER = gql`
  mutation RegisterUser($email: String!, $password: String!) {
    registerUser(email: $email, password: $password) {
      id
      email
    }
  }
`;

function Register() {
  // react-hook-form 表单初始化
  const { register, handleSubmit, formState: { errors } } = useForm();
  // 路由跳转Hook初始化
  const navigate = useNavigate();

  // 👉 useMutation 必须在组件顶层调用,不能放在onSubmit内部
  const [registerUser, { loading, clientError }] = useMutation(REGISTER_USER, {
    // 配置请求成功后的统一逻辑
    onCompleted: () => {
      // 注册成功后跳转登录页
      navigate('/login');
    }
  });

  // 表单提交回调,仅承载业务触发逻辑
  const onSubmit = async (formData) => {
    // 直接调用顶层拿到的mutation触发函数,传入表单参数
    await registerUser({
      variables: {
        email: formData.email,
        password: formData.password
      }
    });
  };

  return (
    <Form onSubmit={handleSubmit(onSubmit)} className="p-4 shadow-sm rounded">
      <Form.Group className="mb-3" controlId="registerEmail">
        <Form.Label>邮箱</Form.Label>
        <Form.Control 
          type="email" 
          placeholder="输入注册邮箱" 
          {...register('email', { required: '邮箱不能为空' })}
        />
        {errors.email && <Form.Text className="text-danger">{errors.email.message}</Form.Text>}
      </Form.Group>

      <Form.Group className="mb-3" controlId="registerPassword">
        <Form.Label>密码</Form.Label>
        <Form.Control 
          type="password" 
          placeholder="设置登录密码" 
          {...register('password', { required: '密码不能为空', minLength: 6 })}
        />
        {errors.password && <Form.Text className="text-danger">密码长度不能少于6位</Form.Text>}
      </Form.Group>

      <Button variant="primary" type="submit" disabled={loading} className="w-100">
        {loading ? '注册中...' : '完成注册'}
      </Button>

      {clientError && <div className="text-danger mt-2 text-center">注册失败,请检查网络后重试</div>}
    </Form>
  );
}
关键注意事项
  • useMutation返回的第二个值(即上述示例中的registerUser函数)不属于Hook,本质是一个普通的异步请求触发方法,完全可以在onSubmit、按钮点击处理器等普通函数中自由调用,不存在Hook规则限制。
  • 所有Hook的执行顺序必须在组件每次渲染时保持完全一致,不能将Hook放在if判断、循环、事件回调中执行,否则会导致React内部Hook状态索引错乱,出现不可预期的渲染错误。
  • 如果后续需要将注册逻辑抽离复用,可以封装一个命名以use开头的自定义Hook(例如useRegisterUser),ESLint会自动识别其Hook属性,不会触发规则校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.03 23:45:00