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

React登录组件添加dispatch后触发catch及Invalid hook call错误求助

问题:React+Redux登录逻辑触发无效Hook调用错误

现象

原本触发loginHandler()后能正常获取接口响应,但在try块中添加dispatch逻辑后,catch块被执行,同时抛出以下错误:

react-dom.development.js:16227 Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
    at Object.throwInvalidHookError (react-dom.development.js:16227:9)
    at useContext (react.development.js:1618:21)
    at useReduxContext (useReduxContext.js:21:24)
    at useSelector2 (useSelector.js:40:9)
    at LoginHandler (Login.jsx:49:22)

涉及代码:

function Login() {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  const [msg, setMsg] = useState("");

  const dispatch = useDispatch();

  const LoginHandler = async (e) => {
    e.preventDefault();

    try {
      const response = await axios.post(
        LOGIN_URL,
        JSON.stringify({ username, password }),
        {
          headers: { "Content-Type": "application/json" },
          withCredentials: false,
        }
      );
      console.log(JSON.stringify(response?.data?.access));

      dispatch(
        login({
          username: username,
          accessToken: response?.data?.access,
        })
      );
      // 错误来源:在事件处理函数中调用useSelector
      const userName = useSelector((state) => state.user.value.userName);
      const accessToken = useSelector(
        (state) => state.user.value.userAccessToken
      );
      console.log("USER NAME STORED IN STORE = " + userName);
      console.log("USER ACESS TOKEN STORED IN STORE = " + accessToken);

      setMsg("Login Successful!");
    } catch (err) {
      if (!err?.response) {
        console.log("NO SERVER RESPONSE");
        setMsg("NO SERVER RESPONSE!");
      } else {
        console.log("SOMETHING WRONG!");
        setMsg("SOMETHING WENT WRONG!");
      }
    }
  };

  return (
    <LoginForm />
  );
}

export default Login;

原因

根本问题是违反了React Hook的使用规则:

  • useSelector属于React Hook,必须在函数组件的顶层作用域调用,不能在事件处理函数、循环、条件判断或嵌套函数内部使用。
  • 你在LoginHandler这个事件处理函数里调用了useSelector,直接触发React的Hook校验错误,这个错误是同步抛出的,导致try块执行中断,直接进入catch块。

解决方案

修正步骤:

  1. 将useSelector移到组件顶层,符合Hook使用规范;
  2. 事件处理函数中直接使用已有变量(不需要立即从Redux取状态,因为dispatch后Redux状态更新是异步的,组件需要重新渲染才能拿到最新值);
  3. 若需监听Redux状态变化,使用useEffect钩子。

修正后的代码:

function Login() {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [msg, setMsg] = useState("");

  const dispatch = useDispatch();
  // 移到组件顶层,符合Hook规则
  const userName = useSelector((state) => state.user.value.userName);
  const accessToken = useSelector((state) => state.user.value.userAccessToken);

  // 监听Redux状态变化,更新后执行打印逻辑
  useEffect(() => {
    if (userName && accessToken) {
      console.log("USER NAME STORED IN STORE = " + userName);
      console.log("USER ACESS TOKEN STORED IN STORE = " + accessToken);
    }
  }, [userName, accessToken]);

  const LoginHandler = async (e) => {
    e.preventDefault();

    try {
      const response = await axios.post(
        LOGIN_URL,
        JSON.stringify({ username, password }),
        {
          headers: { "Content-Type": "application/json" },
          withCredentials: false,
        }
      );
      console.log(JSON.stringify(response?.data?.access));

      dispatch(
        login({
          username: username,
          accessToken: response?.data?.access,
        })
      );
      // 直接使用当前作用域的变量,无需从Redux取
      console.log("USER NAME TO BE STORED = " + username);
      console.log("USER ACESS TOKEN TO BE STORED = " + response?.data?.access);

      setMsg("Login Successful!");
    } catch (err) {
      if (!err?.response) {
        console.log("NO SERVER RESPONSE");
        setMsg("NO SERVER RESPONSE!");
      } else {
        console.log("SOMETHING WRONG!");
        setMsg("SOMETHING WENT WRONG!");
      }
    }
  };

  return (
    <LoginForm />
  );
}

export default Login;

补充说明

  • 即使把dispatch放到另一个函数里,只要这个函数内部还是在事件处理流程中调用useSelector,依然会触发同样的错误——核心是Hook不能在非顶层作用域调用,和dispatch的位置无关。
  • Redux状态更新后,组件会重新渲染,此时顶层的useSelector会拿到最新状态,通过useEffect可以监听这个变化并执行后续逻辑。

内容的提问来源于stack exchange,提问作者Rahul Kumar S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 19:15:31