react中得forwardref作用是啥,仅仅是因为函数组件不能直接

react中得forwardref作用是啥,仅仅是因为函数组件不能直接
最新回答
忘记心痛回忆

2026-05-21 03:50:35

大家好,我卡颂。

在 React 的新文档中,有关于 useRef 和 useEffect 这两个 API 的介绍被放在了“Escape Hatches(逃生舱)”这一章节,这说明它们在正常操作中并非常规选项,而是为了应对特殊场景而存在。

如果开发者过度依赖这两个 API,可能会引发问题。因此,理解它们的使用场景至关重要。

今天,让我们来探讨 React 中 Ref 的使用场景。

如果你想加入一个专注于研究人类高质量前端框架的社群,欢迎你。

为什么 Ref 和 effect 被归类在“逃生舱”?

首先,让我们思考一个问题:为什么 useRef 和 effect 被归类在“逃生舱”中?

这是因为它们操作的是“脱离 React 控制的因素”。例如,在 useEffect 中修改了 document.title,因为 document.title 不属于 React 管辖的状态范围,React 无法感知其变化,因此将其归类为 effect。

同样地,使 DOM 聚焦需要调用 element.focus(),这直接执行 DOM API 也是不受 React 控制的。

虽然它们属于“脱离 React 控制的因素”,但为了确保应用的稳健性,React 仍然需要防止这些因素失控。

失控的 Ref

接下来,我们来看看什么是失控的 Ref。

在不失控的情况下,尽管我们操作了 DOM,但涉及的都是“React 控制范围外的因素”,因此不算失控。

然而,当情况变为:

此时,尽管我们同样操作了 DOM,但这些操作属于“React 控制范围内的因素”,通过 ref 执行这些操作就属于失控的情况。

举例来说,让我们参考 React 文档中的例子。当点击按钮时,会插入或移除 P 节点。

第一个按钮通过 React 控制的方式移除 P 节点。

而第二个按钮直接操作 DOM 移除 P 节点。

如果这两种“移除 P 节点”的方式混用,那么先点击第一个按钮再点击第二个按钮就会引发错误。

这就是“使用 Ref 操作 DOM 造成的失控情况”的一个例子。

如何限制失控

既然我们已经了解了失控的情况,那么如何限制这种失控呢?

在 React 中,组件可以被分为两类:

“低阶组件”指的是那些“基于 DOM 封装的组件”,例如,直接基于 input 节点封装的组件。

在“低阶组件”中,可以将 ref 指向 DOM,例如:

而“高阶组件”则指的是那些“基于低阶组件封装的组件”,例如,基于 Input 组件封装的 Form 组件。

“高阶组件”无法直接将 ref 指向 DOM,这一限制确保了“ref 失控”的范围仅限于单个组件内,从而避免了跨组件的“ref 失控”。

以文档中的示例为例,如果我们想在 Form 组件中点击按钮,操作 input 聚焦:

点击后,会报错:

这是因为 Form 组件中向 MyInput 传递 ref 失败了,inputRef.current 并未指向 input 节点。

问题根源在于“为了将 ref 失控的范围控制在单个组件内,React 默认情况下不支持跨组件传递 ref”。

人为取消限制

如果确实需要取消这个限制,可以使用 forwardRef API 显式传递 ref。

使用 forwardRef(其中 forward 的意思是“传递”)后,就能实现跨组件传递 ref。

在这个例子中,我们将 inputRef 从 Form 跨组件传递到 MyInput 中,并与 input 产生关联。

在实践中,一些开发者可能觉得 forwardRef 这一 API 有些冗余。

但从“防止 ref 失控”的角度来看,forwardRef 的意图十分明确:既然开发者主动调用 forwardRef 来解除“防止 ref 失控的限制”,那么他应该清楚自己在做什么,并且应该承担相应的风险。

同时,有了 forwardRef 存在,当发生“ref 相关错误”时,也更容易定位错误。

useImperativeHandle

除了“限制跨组件传递 ref”之外,还有一种“防止 ref 失控”的措施,即 useImperativeHandle。它的逻辑是:

既然“ref 失控”是因为“使用了不应被使用的 DOM 方法”(例如,appendChild),那么我们可以限制“ref 中只存在可以被使用的方法”。

使用 useImperativeHandle 来修改我们的 MyInput 组件:

现在,Form 组件中通过 inputRef.current 能获取到如下数据结构:

这就有效地避免了“开发者通过 ref 获取到 DOM 后,执行不应被使用的 API,从而导致 ref 失控”的情况。

总结

通常情况下,Ref 的使用较为少见,它更多是作为一种“逃生舱”存在。

为了防止不当使用导致 ref 失控,React 限制了“默认情况下,不能跨组件传递 ref”。

为了打破这种限制,可以使用 forwardRef。

为了减少 ref 对 DOM 的不当使用,可以使用 useImperativeHandle 来限制 ref 传递的数据结构。