React中传递自定义Hook返回的ref触发ESLint 'Cannot access refs during render'报错的问题排查
我来帮你拆解这个问题哈~
为啥会出现这个ESLint报错?
其实这是react-hooks/refs规则的误报。这条规则本来是用来阻止你在组件渲染阶段直接访问ref.current的——毕竟current的值在渲染期间可能还没更新,用它来渲染UI会导致页面状态不一致。但你这里只是把ref对象本身传递给组件,根本没碰current属性,这种用法其实完全安全。
那为啥解构的写法没问题?这是因为ESLint的检测逻辑有点“死板”:当你用dialog.ref这种方式从Hook返回的对象里取ref时,它会直接触发警告;但你用解构const { ref } = useDialog()时,规则没把这个变量和“不安全的ref访问”关联起来,所以就放过了。
怎么解决这个问题?
给你几个适配你需求的方案:
1. 直接禁用该行的ESLint规则
既然你确认这个用法是安全的,最直接的办法就是在报错的那行加个注释跳过检测:
<Dialog ref={dialog.ref} /> {/* eslint-disable-next-line react-hooks/refs */}
2. 用中间变量暂存ref
有时候把dialog.ref赋值给一个中间变量,就能绕过ESLint的检测逻辑,写法也很简单:
export function MyComponent() { const dialog = useDialog(); const dialogRef = dialog.ref; // 先把ref存到中间变量里 return ( <> <Dialog ref={dialogRef} /> {/* 这样就不会报错啦 */} </> ) }
3. 修复Dialog组件的ref转发(重要!)
另外得提醒你一个容易忽略的点:你的Dialog组件现在根本没正确转发ref!你用了ComponentPropsWithRef<"dialog">但没配合forwardRef,导致你传递给Dialog的ref会被当成普通props,根本绑不到内部的原生<dialog>元素上。得改成这样:
import { forwardRef, ComponentPropsWithoutRef } from 'react'; export type DialogProps = ComponentPropsWithoutRef<"dialog">; export const Dialog = forwardRef<HTMLDialogElement, DialogProps>(({ className = "", ...props }, ref) => { return ( <dialog ref={ref} className={`ui-dialog ${className}`} {...props} /> ); }); // 别忘了加displayName,不然React DevTools里会显示成ForwardRef,调试起来很麻烦 Dialog.displayName = "Dialog";
改完之后,useDialog里的ref.current才能真正拿到原生的dialog DOM节点,你写的handleOpen、handleClose才能正常工作。
最后补充一句
虽然这个规则偶尔会误报,但它大部分时候还是很有用的——能帮你避免在渲染阶段依赖ref.current的值(比如用它来计算UI),毕竟ref更新不会触发组件重渲染,很容易导致UI和实际状态脱节。但仅仅传递ref对象本身是完全安全的,所以遇到这种误报时,直接禁用单条规则是完全合理的选择~
内容的提问来源于stack exchange,提问作者Joe

