NextJS 13.4.7中useMediaQuery客户端钩子报错问题咨询
NextJS 13.4.7 客户端组件触发'use client-only hook'错误排查
问题描述
使用NextJS 13.4.7开发应用,Header组件已添加"use client"声明作为客户端组件,被服务端组件引入,但调用自定义useMediaQuery钩子时仍持续收到'use client-only hook'错误,官方文档表明该用法应被允许。
核心代码
"use client"; import { useContext, useState, useEffect } from "react"; import PageDataContext from "@/contexts/PageDataContext"; import useMediaQuery from "@/hooks/useMediaQuery"; import { motion } from "framer-motion"; export default function Header() { const content = useContext(PageDataContext); const isSmallDevice = useMediaQuery("only screen and (max-width : 991px)"); const [hideNav, setHideNav] = useState(isSmallDevice); const { home: homeContent } = content; const { navigation: navigationContent } = content; const { name } = homeContent; const [firstName, lastName] = name.split(" "); const variants = { open: { opacity: 1, x: "0%" }, closed: { opacity: 0, x: "100%" }, }; const toggleSideNav = () => { setHideNav(!hideNav); }; useEffect(() => { setHideNav(isSmallDevice); }, [isSmallDevice]); return ( <header id="site_header" className="header"> <div className="header-content clearfix"> <div className="text-logo"> <a href="/"> <div className="logo-symbol">{firstName[0].toUpperCase()}</div> <div className="logo-text"> {firstName} <span>{lastName}</span> </div> </a> </div> <motion.div animate={hideNav ? "closed" : "open"} variants={variants} style={{ position: "fixed", width: "100%", height: "calc(100% - 52px)", top: "52px", left: "auto", }} > <div className="site-nav"> <ul className="leven-classic-menu site-main-menu"> {navigationContent.map((item, idx) => ( <li className="menu-item" key={"header_key_" + idx}> <a href={item.url}>{item.title}</a> </li> ))} </ul> </div> </motion.div> <a className="menu-toggle mobile-visible" onClick={toggleSideNav}> <i className="fa fa-bars"></i> </a> </div> </header> ); }
排查与解决步骤
1. 检查自定义useMediaQuery钩子的客户端标记
自定义钩子useMediaQuery内部必然用到了useEffect/useState等客户端专属API,但如果钩子所在文件未添加"use client"声明,默认会被当作服务端组件处理,调用时就会触发错误。
解决: 在@/hooks/useMediaQuery文件顶部添加"use client";声明。
2. 验证上下文提供者的执行环境
如果PageDataContext.Provider所在的组件是服务端组件,且上下文值包含非序列化内容(如函数、DOM对象),或者提供者本身用到了客户端API但未标记"use client",会间接导致客户端组件的上下文调用报错。
解决:
- 确保
PageDataContext.Provider所在组件若使用客户端API,需添加"use client"; - 上下文传递的
content必须是可序列化的JSON数据,避免包含非序列化对象。
3. 检查组件引入链路的服务端逻辑
若服务端组件在引入Header时,存在条件渲染依赖客户端状态、将Header作为参数传递给服务端函数等操作,会导致NextJS误判组件执行环境。
解决: 服务端组件仅直接引入并渲染Header,不在服务端逻辑中处理Header的客户端相关状态。
4. 清理缓存与重启开发服务
NextJS 13.4.x部分小版本存在客户端组件标记的缓存bug,尝试:
- 删除
.next文件夹及node_modules/.cache目录; - 重启开发服务器;
- 确认
next.config.js未禁用React Strict Mode(Strict Mode可能放大潜在bug)。
代码优化建议
原代码中useState初始化后又用useEffect同步isSmallDevice的逻辑可简化,避免状态不同步:
// 替换原有的hideNav状态与useEffect const [isManuallyToggled, setIsManuallyToggled] = useState(false); // 优先使用手动切换的状态,否则遵循媒体查询结果 const hideNav = isManuallyToggled ? !isSmallDevice : isSmallDevice; const toggleSideNav = () => { setIsManuallyToggled(!isManuallyToggled); };
内容的提问来源于stack exchange,提问作者Gaurang
相关产品推荐
相关产品推荐

