Next.js使用自定义组件触发ref警告与Hydration水合失败报错
问题原因
两个报错是同一根因引发的连锁问题:
- Next.js 的
<Link />组件默认会给直接子元素透传ref属性,用来实现路由预加载、点击事件绑定等内置逻辑。你的代码里把自定义函数组件HeaderIcon直接作为Link的子节点,但普通函数组件默认不支持接收ref,所以控制台抛出了「Function components cannot be given refs」的警告。 - ref透传失败会直接导致
Link在服务端渲染、客户端水合两个阶段输出的DOM结构不一致:服务端会给无法接收ref的子元素自动套一层原生<a>标签做兜底,客户端水合时不会生成这层结构,DOM匹配失败就触发了水合错误,最终导致整个应用根节点降级为纯客户端渲染。
其余没有被Link包裹的HeaderIcon实例不会触发这个问题,你用到的react-icons图标组件本身已经做了ref转发支持,不是报错来源。
修复方案
你可以任选以下一种方案修复:
方案1:调整Link子元素结构(改动最小)
不要让自定义函数组件作为Link的直接子节点,在中间插入一层原生DOM元素承接ref,将原本包裹在外侧的样式类移到原生标签上即可:
<div className="flex items-center space-x-6"> {/* 把原本在外侧div上的样式类移到Link内部的原生标签上 */} <Link href="/"> <a className="headerIcons active:opacity-80"> <HeaderIcon inactiveIcon={<AiOutlineHome />} activeIcon={<AiFillHome />} /> </a> </Link> {/* 其余HeaderIcon无需修改 */} <div className="headerIcons"> <HeaderIcon inactiveIcon={<MdOutlineAddBox />} activeIcon={<MdAddBox />} /> </div> <div className="headerIcons -rotate-12"> <HeaderIcon inactiveIcon={<AiOutlineNotification />} activeIcon={<AiFillNotification />} /> </div> <div className="cursor-pointer"> <Avatar w={7} h={7} /> </div> </div>
如果使用Next.js 13+版本,Link组件不再强制要求包裹a标签,你也可以用原生
<div>作为中间承接层,效果一致。
方案2:给HeaderIcon添加forwardRef支持(一劳永逸)
修改HeaderIcon组件,通过React.forwardRef包裹组件,将传入的ref绑定到内部根DOM节点上,让组件可以正常接收外部透传的ref:
import { useState, forwardRef } from 'react'; // 用forwardRef包裹组件,第二个参数为接收的ref const HeaderIcon = forwardRef(function HeaderIcon({ inactiveIcon, activeIcon }, ref) { const [isActive, setIsActive] = useState(false); return ( // 将ref绑定到根div上 <div ref={ref} onClick={() => setIsActive(!isActive)}> {isActive ? activeIcon : inactiveIcon} </div> ); }); export default HeaderIcon;
修改后不需要调整原有Link的写法,组件在任何需要透传ref的场景下都能正常工作,不会再触发相关警告和水合错误。
内容的提问来源于stack exchange,提问作者Chee Thong
相关产品推荐
相关产品推荐

