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

Next.js+Typescript项目添加Loader触发Invalid hook call错误如何解决

问题排查和修复方案

1. 触发Invalid hook call报错的直接原因

你在Index.tsx中把useLoading、useRouter、useEffect全部写在了Home函数组件的外部,React 明确规定Hook只能在函数组件的顶层作用域或者自定义Hook内部调用,不能在组件外的全局作用域调用。


2. 其他需要修复的逻辑问题

2.1 路由事件解绑逻辑错误

你给router.events.off传入的是新创建的匿名函数,和on阶段传入的回调不是同一个引用,会导致事件解绑失败,出现内存泄漏。

2.2 路由事件监听不全

仅监听routeChangeStart事件无法覆盖所有路由跳转场景,还需要补充监听routeChangeComplete(路由跳转成功)和routeChangeError(路由跳转失败)事件来结束加载状态,否则跳转完成后Loader不会消失。

2.3 自定义Hook错误提示文本错误

useLoading中的报错提示写的是useCount must be used within a CountProvider,和实际的Hook、Provider名称不符,排查问题时会造成误导。

2.4 缺少Provider全局包裹

没有把整个Next.js应用包裹在LoadingProvider中,哪怕修复了Hook调用位置的问题,也会触发useLoading的抛出错误。

2.5 Loader组件依赖缺失

Loader.tsx缺少NextPage类型导入和样式文件导入,会触发类型报错和样式找不到的问题。


修复后的完整代码

修复后 Index.tsx

import Loader from "../components/Loader/Loader";
import { useLoading } from "../context/LoadingContext";
import { useRouter } from "next/router";
import { useEffect } from "react";
import type { NextPage } from "next";

const Home: NextPage = () => {
  // 所有Hook移到组件内部顶层调用
  const { state, dispatch } = useLoading();
  const router = useRouter();

  useEffect(() => {
    // 统一声明回调,保证绑定和解绑用的是同一个函数引用
    const handleStart = () => dispatch({ type: "startLoading" });
    const handleEnd = () => dispatch({ type: "endLoading" });
    const handleError = () => dispatch({ type: "endLoading" });

    router.events.on("routeChangeStart", handleStart);
    router.events.on("routeChangeComplete", handleEnd);
    router.events.on("routeChangeError", handleError);

    return () => {
      router.events.off("routeChangeStart", handleStart);
      router.events.off("routeChangeComplete", handleEnd);
      router.events.off("routeChangeError", handleError);
    };
  }, [dispatch, router.events]);

  useEffect(() => {
    // 初始页面加载完成后关闭加载状态
    dispatch({ type: "endLoading" });
  }, [dispatch]);

  return (
    <>
      {state.loading ? (
        <Loader />
      ) : (
        <>
          <div>
            {/* 其他业务组件 */}
          </div>
        </>
      )}
    </>
  );
};

export default Home;

修复后 LoadingContext.ts 错误提示部分

export function useLoading() {
  const context = React.useContext(LoadingContext);
  if (context === undefined) {
    // 修正错误提示文本
    throw new Error("useLoading must be used within a LoadingProvider");
  }
  return context;
}

新增 pages/_app.tsx 全局包裹Provider

import type { AppProps } from "next/app";
import LoadingProvider from "../context/LoadingContext";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <LoadingProvider>
      <Component {...pageProps} />
    </LoadingProvider>
  );
}

修复后 Loader.tsx

import type { NextPage } from "next";
import styles from './Loader.module.css' // 替换为你的实际样式文件路径

const Loader: NextPage = () => {
  return (
    <div className={styles.loader_wrapper}>
      <div className={styles.content}>
        <span className={styles.loading_text}>Loading...</span>
      </div>
    </div>
  );
};

export default Loader;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 15:27:05