
react 的 ref 回调函数在 dom 元素挂载时接收真实节点,在卸载时接收 null,这是 react 保证 ref 状态与 dom 生命周期严格同步的机制,并非类型错误或 bug。
在使用函数式 ref(如 ref={el => ...})时,React 会主动且确定地在两个关键时机调用该回调:
✅ 挂载时:DOM 元素成功插入文档后,传入对应的 HTMLInputElement 实例;
❌ 卸载时:元素被移除(例如数组长度变化、条件渲染导致组件消失、父组件重渲染等),React 会再次调用该 ref 回调,并传入 null —— 这是为了显式通知你“该引用已失效”,避免悬挂引用(dangling reference)和内存泄漏。
因此,你代码中 el: HTMLInputElement | null 的类型是完全正确且必要的。若忽略 null 分支,直接赋值 inputsRef.current[i] = el,虽然运行时通常无异常,但会在元素卸载后将 null 写入数组,后续若未做空值检查就调用 inputsRef.current[i]?.focus() 等方法,将导致静默失败或运行时错误。
✅ 正确写法(安全、健壮):
export default function SomeArray() {
const inputsRef = useRef([]);
return (
<>
{[1, 2, 3].map((_, i) => (
{
if (el) {
// 挂载:存入有效节点
inputsRef.current[i] = el;
} else {
// 卸载:清理对应位置,避免残留 null 或旧节点
inputsRef.current[i] = null;
}
}}
/>
))}
>
);
} ? 关键注意事项:
- 必须添加 key:map 渲染列表时缺失 key 会导致 React 复用 DOM 节点,ref 回调可能被错误触发(如本应卸载却未收到 null),引发 ref 数组错位;
- 不要依赖 useRef 初始值做逻辑判断:inputsRef.current 初始化为空数组,但其长度不随渲染自动更新,需手动维护(如配合 useEffect 清理,或改用 createRef + 数组映射);
-
批量操作前建议过滤 null:例如聚焦第一个有效输入框时,应写为
const firstValid = inputsRef.current.find(el => el !== null); firstValid?.focus();
总结:null 不是“意外”,而是 React 的契约式设计——它赋予你精确控制引用生命周期的能力。拥抱 HTMLInputElement | null 类型,主动处理 null 分支,是编写可维护、可预测 React DOM 交互代码的必要实践。










