Formik使用fetch提交GraphQL表单触发react-hooks规则报错
问题描述
使用React + Formik + fetch提交GraphQL mutation(后端基于Neo4j GraphQL库、Apollo Server搭建)时,触发React Hooks规则校验报错:
第28行第18列触发react-hooks/rules-of-hooks规则校验:React Hook "useFormik" 被调用在函数"updateNameFetch"内部,该函数既不是React函数组件,也不是自定义React Hook函数;其中React组件名需以大写字母开头,React Hook名称需以"use"开头。
错误原因
- 违反React Hooks核心调用规则:Hook只能在React函数组件顶层、或者名称以
use开头的自定义Hook顶层调用,不能嵌套在普通函数、条件判断、循环内部。 - 代码存在括号闭合错误:
updateNameFetch普通异步函数声明后没有正确闭合大括号,导致原本应该在App组件顶层调用的useFormik,被错误嵌套在了updateNameFetch函数内部,触发规则校验。 - 逻辑缺陷:GraphQL mutation的query字符串定义在
updateNameFetch内部,但在formik的onSubmit回调中直接引用,实际提交时既拿不到动态拼接的参数,还存在GraphQL注入风险。
修复方案
调整代码结构,修正括号闭合问题,把动态生成mutation的逻辑移到onSubmit回调内部,修复后的完整代码如下:
import './App.css'; import { useFormik } from "formik" function App() { // useFormik放在App组件顶层调用,符合Hooks规则 const formik = useFormik({ initialValues:{ name:"", age: "", sex: "", weight:"", smoking:"", drinking:"", nationality:"", birth_type:"", }, onSubmit: async (values) => { // 提交时动态拼接mutation参数,避免作用域问题 const query = JSON.stringify({ query: `mutation { createUsers(input:{ user_name: "${values.name}" age: ${values.age} sex: "${values.sex}" weight: ${values.weight} smoking:"${values.smoking}" drinking: "${values.drinking}" nationality:"${values.nationality}" birth_type: "${values.birth_type}" } ){ users{ user_name } } } ` }); // 发起fetch请求,接口地址根据实际部署情况替换为带协议的完整路径或本地代理路径 const res = await fetch("graphql-newone.herokuapp.com/",{ method: "POST", headers: {"Content-Type": "application/json"}, body: query, }) const resData = await res.json() console.log('提交返回结果', resData) } }) return ( <div className="App"> <form onSubmit={formik.handleSubmit}> <div className='input-container'> <input id="name" name="name" type="text" placeholder="Name" onChange={formik.handleChange} value = {formik.values.name} /> <input id="sex" name="sex" type="text" placeholder="Sex" onChange={formik.handleChange} value = {formik.values.sex} /> <input id="age" name="age" type="text" placeholder="Age" onChange={formik.handleChange} value = {formik.values.age} /> <input id="weight" name="weight" type="text" placeholder="weight" onChange={formik.handleChange} value = {formik.values.weight} /> <input id="smoking" name="smoking" type="text" placeholder="smoking" onChange={formik.handleChange} value = {formik.values.smoking} /> <input id="drinking" name="drinking" type="text" placeholder="drinking" onChange={formik.handleChange} value = {formik.values.drinking} /> <input id="nationality" name="nationality" type="text" placeholder="nationality" onChange={formik.handleChange} value = {formik.values.nationality} /> <input id="birth_type" name="birth_type" type="text" placeholder="birth_type" onChange={formik.handleChange} value = {formik.values.birth_type} /> </div> <button type="submit">Submit</button> </form> </div> ); } export default App;
修复要点
- 将
useFormik调用移到App组件最顶层,和组件return语句同级,完全符合Hooks调用规则,不会再触发eslint的rules-of-hooks报错。 - 删除未实际调用的
updateNameFetch函数,把mutation拼接逻辑移到onSubmit回调内部,直接从formik的values参数取表单值,解决作用域引用错误。 - 给fetch请求加了await和返回值解析,方便调试接口返回结果。
- 修正原代码多余的闭合大括号,解决函数嵌套错误,同时补全了原代码遗漏的drinking输入框绑定。
内容的提问来源于stack exchange,提问作者daylightisminetocommand
相关产品推荐
相关产品推荐

