
在 react 中,ref 回调函数(如 `ref={el => {...}}`)会在 dom 元素挂载时传入真实节点,卸载时传入 `null`,这是 react 内置的生命周期行为,用于确保 ref 始终准确反映 dom 状态。
当你使用函数式 ref(即 ref={el => inputsRef.current[i] = el})时,React 会在两个关键时机调用该回调:
- ✅ 挂载阶段:DOM 元素首次插入文档后,React 传入对应的 HTMLInputElement 实例;
- ❌ 卸载阶段:当该元素被移除(例如数组长度变化、条件渲染导致组件消失、父组件重渲染等),React 会再次调用该回调,并传入 null,以显式通知你“这个 ref 已失效”。
这就是为什么 el 类型为 HTMLInputElement | null —— TypeScript 正确地反映了这一双向生命周期语义。
正确处理 null 的推荐写法
export default function SomeArray() {
const inputsRef = useRef([]);
return (
<>
{[1, 2, 3].map((_, i) => (
{
if (el) {
// 挂载:安全赋值
inputsRef.current[i] = el;
} else {
// 卸载:清理引用,防止内存泄漏或 stale node
inputsRef.current[i] = null;
}
}}
/>
))}
>
);
} 关键注意事项
- 必须添加 key:若省略 key,React 可能复用 DOM 节点,导致 ref 回调被错误触发(例如旧索引收到 null,新索引未及时更新),引发 inputsRef.current 数据错乱;
-
初始化 ref 数组需谨慎:useRef
([]) 是安全的,但不要预先填充 null(如 new Array(3).fill(null)),因为 ref 回调本身已负责状态同步; - 避免直接解构或读取未挂载项:访问 inputsRef.current[i] 前应做空值检查(如 if (inputsRef.current[i]) { ...focus() }),尤其在副作用(如 useEffect)中;
-
替代方案考虑:对动态列表,也可用 useRef
>({}) 或结合 useCallback + createRef(),但函数式 ref 仍是标准且最可控的方式。
简言之,null 不是类型缺陷,而是 React 精确控制 DOM 生命周期的契约体现——尊重它,才能写出健壮、可预测的 ref 管理逻辑。










