Relay使用useMutation报Invalid hook call错误如何解决
问题根因
报错的核心原因是违反React Hooks调用规则:useMutation属于React Hook,仅允许在函数组件/自定义Hook的顶层作用域调用,不能在普通工具函数、事件回调、条件分支、循环嵌套中调用。
你之前的写法把useMutation封装到了普通工具函数CreateProjectMutation中,还在按钮点击触发的submitProject事件回调里调用这个普通函数,完全不符合Hook的调用要求,所以触发Invalid hook call报错。
修复方法
核心原则:Hook永远在组件顶层调用,事件回调里只执行Hook返回的commit提交方法,不要在回调里调用Hook本身。
- 不需要把整个mutation逻辑封装成独立的普通函数,可以把graphql的mutation定义单独抽离,但是
useMutation的调用必须放在组件顶层 - 点击提交时,只需要调用
useMutation返回的commit方法,传入对应变量和回调即可
修复后完整代码
import React, { useState } from 'react'; import { graphql, useMutation } from 'react-relay'; // 可单独抽离graphql mutation定义,不违反规则 const CREATE_PROJECT_MUTATION = graphql` mutation CreateProjectMutation($input:createProjectInput!) { createProject(input:$input) { project { id content leader } } } `; export default function CreateProject() { const [leader, setLeader] = useState(""); const [content, setContent] = useState(""); const [date, setDate] = useState(""); // 组件顶层调用useMutation,拿到提交方法和加载状态 const [commitCreateProject, isCreateLoading] = useMutation(CREATE_PROJECT_MUTATION); const submitProject = () => { const variables = { input: { content, leader, startDate: date, } }; commitCreateProject({ variables, onCompleted: (res) => { console.log('提交成功', res); // 提交成功后再清空表单,避免请求未完成就丢失用户输入 clearForm(); }, onError: (err) => { console.error('提交失败', err); } }); } const clearForm = () => { setLeader(""); setContent(""); setDate(""); } return ( <div> <label>Leader</label> <input value={leader} onChange={(event) => { setLeader(event.target.value) }} /> <label>content</label> <input value={content} onChange={(event) => { setContent(event.target.value) }} /> <label>start date</label> <input value={date} onChange={(event) => { setDate(event.target.value) }} /> <button onClick={submitProject} disabled={isCreateLoading}> {isCreateLoading ? '提交中...' : 'Submit'} </button> </div> ) }
避坑提示:不要把
clearForm()直接写在commitCreateProject调用的后面,commit方法是异步执行的,直接同步调用清空表单会导致请求还未返回就丢失用户输入,正确做法是放到onCompleted回调中,等接口返回成功后再执行清空操作。
useMutation返回的第二个值是请求的加载状态(示例中重命名为isCreateLoading),可以直接用来控制按钮禁用、展示加载提示,不需要额外手动维护loading状态- 如果需要在多个组件复用同一个mutation逻辑,可以将相关逻辑封装为自定义Hook(函数名以
use开头),自定义Hook内部允许调用其他Hook,使用时同样需要遵循Hook规则在组件顶层调用。
内容的提问来源于stack exchange,提问作者yuner
相关产品推荐
相关产品推荐

