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

Next.js点击按钮触发动画跳转时Hook报错问题求助

解决Next.js中点击按钮跳转时的Invalid Hook Call错误

问题描述

我用最新版Next.js搭建作品集框架,想要实现点击按钮后的流程:页面内容淡出 → 显示Loading_ui组件动画 → 预加载目标页面后延迟跳转。目前HomeButtons组件点击按钮能给body添加fade-out类,但延迟后触发「Invalid hook call」错误,原因是在setTimeout里调用usePathname违反了Hook规则,求解决办法。

错误信息

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. React与渲染器(如React DOM)版本不匹配
  2. 违反了Hook规则
  3. 应用中存在多个React副本

核心问题分析

Hook必须在函数组件的顶层作用域调用,不能嵌套在定时器、条件判断等逻辑内部。另外原代码逻辑存在错误:用当前路径判断跳转目标完全逻辑倒置,点击按钮时直接明确目标路径即可,无需判断当前路径。

解决代码及说明

修改后的HomeButtons组件代码:

import { useRouter } from "next/navigation";
import styles from "./homeButtons.module.css";
import React, { useState, useEffect } from "react";
import Loading_ui from "./Loading_ui"; // 引入Loading组件

export default function HomeButtons() {
  const router = useRouter();
  const [targetPath, setTargetPath] = useState(null); // 存储要跳转的目标路径

  // 通用点击事件,接收目标路径参数
  const handleButtonClick = (path) => {
    setTargetPath(path);
  };

  useEffect(() => {
    if (targetPath) {
      // 触发页面淡出效果,修改标题
      document.body.classList.add("fade-out");
      document.title = "Loading...";

      const animationDuration = 500;
      const timer = setTimeout(() => {
        // 执行跳转
        router.push(targetPath);
        // 跳转后重置状态并清理样式
        setTargetPath(null);
        document.body.classList.remove("fade-out");
      }, animationDuration + 100);

      // 组件卸载时清理定时器,避免内存泄漏
      return () => clearTimeout(timer);
    }
  }, [targetPath, router]);

  return (
    <div>
      <button onClick={() => handleButtonClick("/mywork")} id="my_work" className={styles.cybr_btn}>
        My Work<span aria-hidden>_</span>
        <span aria-hidden className={styles.cybr_btn__glitch}>
          爪ㄚ 山ㄖ尺Ҝ
        </span>
        <span aria-hidden className={styles.cybr_btn__tag}>
          R25
        </span>
      </button>

      <button onClick={() => handleButtonClick("/me")} id="me" className={styles.cybr_btn}>
        Me<span aria-hidden>_</span>
        <span aria-hidden className={styles.cybr_btn__glitch}>
          爪ㄖ
        </span>
        <span aria-hidden className={styles.cybr_btn__tag}>
          R25
        </span>
      </button>

      {/* 跳转过程中显示Loading动画 */}
      {targetPath && <Loading_ui />}
    </div>
  );
}

关键优化点

  • 合并重复的点击事件,通过参数传递目标路径,减少冗余代码
  • 用targetPath状态替代原animationActive,同时标记动画状态和存储跳转目标,逻辑更清晰
  • 移除了违规在定时器内调用的usePathname,完全符合Hook规则
  • 添加定时器清理函数,避免组件卸载后定时器仍执行的问题
  • 跳转后自动移除fade-out类,防止回到首页时页面状态异常
  • 动态渲染Loading_ui组件,实现跳转过程中的动画展示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 17:52:33