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技术栈:
- 将GraphQL Mutation语句、
useMutation初始化调用移动到Register组件的顶层作用域,拿到暴露的请求触发函数。 - 在
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
相关产品推荐
相关产品推荐

