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

React模态框表单集成授权时出现Invalid Hook call错误求助

解决React模态框表单授权中的Invalid Hook Call错误

问题描述

在模态框内的表单实现用户授权功能,UI功能正常,但添加useMutation授权逻辑后触发Invalid Hook call错误,已排除React版本不匹配问题。

相关代码

LoginModal组件

const LoginModal = (props) => {
  const [loginData, setLoginData] = useState({ email: "", password: "" });
  const [validated] = useState(false);

  const [login, { error }] = useMutation(LOGIN_USER);

  const userInputHandler = (event) => {
    const { name, value } = event.target;
    setLoginData({ ...loginData, [name]: value });
  };

  const handleSubmit = async (event) => {
    event.preventDefault();

    const form = event.currentTarget;
    if (form.checkValidity() === false) {
      event.preventDefault();
      event.stopPropagation();
    }
    try {
      const { data } = await login({
        variables: { ...loginData },
      });
      Auth.login(data.login.token);
    } catch (err) {
      console.error(err);
    }
    setLoginData({
      username: "",
      email: "",
      password: "",
    });
  };

  return (
    <div>
      <div className="flex items-center justify-center md:z-100 min-h-screen bg-rose-50">
        <form noValidate validated={validated} onSubmit={handleSubmit}>
          <div className="relative flex flex-col m-6 space-y-10 bg-white shadow-2xl rounded-2xl md:flex-row md:space-y-0 md:m-0">
            {/* left side */}
            <div className="p-6 md-20">
              {/* top content */}
              <h2 className="h2 mb-5 text-4xl font-bold cursor-pointer">
                Login
              </h2>
              <p className="max-w-sm mb-12 font-light text-gray-600">
                Login to see your saved activities!
              </p>
              <div className="space-y-4">
                <input
                  type="text"
                  className="w-full space-y-4 p-6 border border-gray-300 rounded-md placeholder:font-light "
                  placeholder="Email"
                  onChange={userInputHandler}
                  value={loginData.email}
                />
                <input
                  type="text"
                  className="w-full space-y-4  p-6 border border-gray-300 rounded-md placeholder:font-light "
                  placeholder="Password"
                  onChange={userInputHandler}
                  value={loginData.password}
                />
              </div>

              {/* Middle Content */}
              <div className="flex flex-col items-center justify-between mt-6 space-y-6 md:flex-row md:space-y-0">
                <div
                  className="font-thin text-cyan-700 cursor-pointer"
                  onClick={props.SwitchModal}
                >
                  Sign Up
                </div>
                <button disabled={!loginData.email && loginData.password} className="w-full md:w-auto flex justify-center items-center p-6 space-x-4 font-bold text-white rounded-md px-9 bg-cyan-700 shadow-cyan-100 hover:bg-opacity-90 shadow-sm hover:shadow-lg border transition hover:-translate-y-0.5 duration-150 ">
                  <span>Next</span>
                  <FaHandPointRight />
                </button>
              </div>
              {/* Border */}
              <div className="mt-12 border-b border-b-gray-300"> </div>
              {/* Bottom Content */}
              <p className="py-6 text-sm font-thin text-center text-gray-400">
                You can also login with
              </p>
              <div className="flex flex-col space-x-0 space-y-6 md:flex-row md:space-x-4 md:space-y-0">
                <button className="flex items-center justify-center py-2 space-x-3 border border-gray-300 rounded shadow-sm hover:bg-opacity-30 hover:shadow-lg hover:-translate-y-0.5 transition duration-150 md:w-1/2 ">
                  <FaFacebook />
                  <span className="font-thin">Facebook</span>
                </button>
                <button className="flex items-center justify-center py-2 space-x-3 border border-gray-300 rounded shadow-sm hover:bg-opacity-30 hover:shadow-lg hover:-translate-y-0.5 transition duration-150 md:w-1/2 ">
                  <FcGoogle />
                  <span className="font-thin">Google</span>
                </button>
              </div>
            </div>

            {/* right side  */}
            <img src={Chairman} alt="" className="w-[430px] hidden md:block" />
            {/* Close button */}
            <div className="group absolute -top-5 right-4 md:top-4 hover:cursor-pointer hover:-translate-y-0.5 transition duration-150">
              <FaWindowClose
                onClick={props.onShowModal}
                className="group-hover:text-gray-600"
              />
            </div>
          </div>
        </form>
      </div>
    </div>
  );
};

export default LoginModal;

Mutation定义

import { gql } from "@apollo/client";

export const LOGIN_USER = gql`
  mutation login($email: String!, $password: String!) {
    login(email: $email, password: $password) {
      token
      user {
        _id
        username
      }
    }
  }
`;

export const ADD_USER = gql`
  mutation addUser($username: String!, $email: String!, $password: String!) {
    addUser(username: $username, email: $email, password: $password) {
      token
      user {
        _id
        username
        email
      }
    }
  }
`;

export const SAVE_TASK = gql`
  mutation saveTask($input: SavedTaskInput) {
    saveTask(input: $input) {
      _id
      username
      email
      savedTasks {
        activity
        type
        participants
        price
        link
        key
        accessibility
      }
    }
  }
`;

export const REMOVE_TASK = gql`
  mutation removeTask($key: String!) {
    removeTask(key: $key) {
      _id
      username
      email
      savedTasks {
        activity
        type
        participants
        price
        link
        key
        accessibility
      }
    }
  }
`;

控制台错误

Invalid Hook Call错误截图

解决方案

排除React版本问题后,按以下步骤排查修复:

  • 确保组件以React方式渲染:检查父组件调用LoginModal的方式,必须使用JSX语法<LoginModal SwitchModal={...} onShowModal={...} />,不能直接作为普通函数调用。
  • 保持Hook在组件顶层:确认useMutation没有放在条件判断、循环或嵌套函数内,你的代码当前已经符合要求,继续保持即可。
  • 检查Apollo Provider包裹情况:useMutation属于Apollo Client Hook,必须在ApolloProvider上下文内使用。确保应用根组件有如下结构:
    import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';
    
    const client = new ApolloClient({
      uri: '你的GraphQL API地址',
      cache: new InMemoryCache(),
    });
    
    function App() {
      return (
        <ApolloProvider client={client}>
          {/* 包含LoginModal的应用组件 */}
        </ApolloProvider>
      );
    }
    
  • 补充输入框name属性:userInputHandler依赖name属性更新loginData,给输入框添加对应属性:
    <input type="email" name="email" ... />
    <input type="password" name="password" ... />
    
  • 修复按钮disabled逻辑:当前逻辑错误,改为disabled={!loginData.email || !loginData.password},确保邮箱或密码为空时按钮禁用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 01:30:47