NextJS普通函数内调用useRouter触发Invalid hook call报错
报错根因拆解
React及Next.js封装的所有Hook(包括useRouter),合规的执行边界不是“任意JS函数的函数体”,而是React函数组件的渲染逻辑上下文、或以use开头命名的自定义Hook函数体,你当前的代码已经完全突破了这个边界:
- 所有代码写在通用工具文件
lib/api.js中,getRouterParams是普通业务工具函数:既不是React函数组件,也不符合自定义Hook必须以use开头的命名规范,执行时脱离了React的Hook校验上下文 - 该函数被后续的异步方法
getTown直接调用,执行场景处于服务端渲染的独立流程、完全不在React组件的渲染调用栈内,触发Hook调用时必然会被校验逻辑拦截,抛出Invalid hook call错误 - 额外的认知偏差:你计划手动用
query-string解析asPath获取查询参数的逻辑是完全冗余的——Next.js初始化路由实例时,已经自动完成了asPath中查询参数的结构化解析,直接读取router.query即可拿到完整参数键值对,不需要重复实现解析逻辑
对应解决思路
按照代码运行的不同场景选择适配的写法,不要强行在非组件上下文调用路由Hook:
组件/页面内获取参数
直接在React函数组件顶层调用useRouter即可,不需要额外封装工具函数:
// pages/xxx.js 或 components/xxx.js import { useRouter } from 'next/router' function TownPage() { // 组件顶层直接调用,完全符合Hook规则 const router = useRouter() // router.query 即为自动解析好的结构化路由参数 const townName = router.query.town return <div>当前页面所属城镇:{townName}</div> }
如果需要复用路由加工逻辑,把封装方法改成符合规范的自定义Hook:
// hooks/useRouterParams.js import { useRouter } from 'next/router' // 自定义Hook必须以use开头,才能合法调用内置Hook export function useRouterParams() { const router = useRouter() // 可自定义参数加工逻辑 return { ...router.query, fullPath: router.asPath } }
在组件中引入这个自定义Hook使用,不会触发任何Hook调用报错。
非组件上下文获取参数
在服务端逻辑、普通工具函数这类完全脱离React渲染流程的场景下,禁止调用任何React Hook,根据运行环境直接获取参数即可:
- SSR流程(getServerSideProps):直接从页面上下文对象中拿参数,无需调用Hook:
export async function getServerSideProps(context) { // context.query 即为已解析好的路由查询参数 const { town } = context.query // 直接把参数传入业务逻辑即可 const townInfo = await getTown(town) return { props: { townInfo } } }
- 客户端工具逻辑:通过浏览器原生API解析当前页面参数,不依赖React上下文:
// 无React依赖的通用工具函数 export function getCurrentPageQuery() { if (typeof window === 'undefined') return {} // 用浏览器原生URL对象解析查询参数,无需额外依赖query-string const currentUrl = new URL(window.location.href) return Object.fromEntries(currentUrl.searchParams.entries()) }
注意:不要再通过
router = withPageRouter(router)这类操作直接修改原路由实例,Next.js的router实例是单例全局对象,直接修改其属性会引发意料之外的状态不一致问题。
内容的提问来源于stack exchange,提问作者sylargaf
相关产品推荐
相关产品推荐

