一个始乱终弃的API —— forwardRef

一个始乱终弃的API —— forwardRef大家好 我卡颂 React 的 API 大多设计的很优雅 比如经典的 this setState 但有一个 API 从诞生之初就争议不断 甚至很多熟练的开发者都不知道这个 API 存在的意义 在最新的 React19 中 官方团队甚至明确提出 会弃用并移

大家好,欢迎来到IT知识分享网。

大家好,我卡颂。

React的API大多设计的很优雅,比如经典的this.setState

但有一个API从诞生之初就争议不断,甚至很多熟练的开发者都不知道这个API存在的意义。

在最新的React19中,官方团队甚至明确提出 —— 会弃用并移除这个API。

这可真是字面意义的「始乱终弃」

他就是forwardRef

他在什么背景下产生?为什么争议不断?最后为什么被弃用?本文就来聊聊forwardRef背后的故事。

forwardRef诞生的初衷

refreference(引用)的缩写。既然是引用,那根据React元素类型的不同自然有不同的引用效果。

比如,传递给React Elementref,可以获得对应原生元素实例的引用

// domRef.current 对应 HTMLInputElement <input ref={domRef} /> 

传递给类组件的ref,可以获得类组件实例的引用

// instanceRef.current 对应 SomeClassComponent实例 <SomeClassComponent ref={instanceRef} /> 

由于函数组件没有对应实例,所以函数组件没有什么可引用的。

「语义」来讲,函数组件自然不能接收ref prop

但是,虽然函数组件不能接收ref prop,但有时函数组件有「转发ref」的需要。

比如,下述代码中的函数组件FC有个子组件inputinput作为React Element,有自己的实例。

function FC() { return <input />; } 

如果FC的祖先元素想获得input实例的引用,就得让FC帮忙转发一下

下述代码中,FC经过forwardRef包裹,就能将子组件input的实例转发给inputRef了:

const FC = forwardRef((props, ref) => { return <input ref={ref}/>; }); <FC ref={inputRef}/> 

forwardRef中的forward从字面意义上讲就是「转发」的意思。

forwardRef的各种坑

forwardRef在使用上会带来很多不便之处,这里举几个例子。

先来看比较不起眼的坑。

如果你像下面这样写,那函数组件本身是个匿名函数,在React Dev Tools中看不到组件名:

// 匿名的函数组件 const FC = forwardRef((props, ref) => { return <input ref={ref}/>; }); 

必须显式的再写一遍组件名才行:

// 具名的函数组件 const FC = forwardRef(function FC(props, ref) { return <input ref={ref}/>; }); 

上面还只是小麻烦,更麻烦的是forwardRefTS结合时造成的类型书写复杂。

如果你的函数组件接收范型类型,当该函数组件被forwardRef包裹,范型推导就会失效。

需要使用一些很hack的类型来断言forwardRef

一个始乱终弃的API —— forwardRef

文章 TypeScript + React: Typing Generic forwardRefs 详细介绍了forwardRef + TS的各种坑

如果说以上坑都只是带来不便,那下面这个坑就是实打实的性能问题了。

由于forwardRef会在组件树中生成额外的层级,在应用比较庞大时可能产生性能问题。

比如,React-Redux的维护者markerikson在给React-Redux做性能基准测试时就发现,forwardRef会对页面FPS指标带来不利影响。

一个始乱终弃的API —— forwardRef

类似上面这些问题使forwardRef在社区的争议一直很大。

一个始乱终弃的API —— forwardRef

官方的态度

官方很早就意识到forwardRef的局限性,早在2019年,sebmarkbage就在RFC #107[1]指出「未来,最终会移除forwardRef」

一个始乱终弃的API —— forwardRef

Andrew在23年也表达过「forwardRef未来一定会被移除」,之所以没有立刻做,是因为「移除一个被大量使用的API属于Breaking Change」

一个始乱终弃的API —— forwardRef

按照Semantic Versioning(即形如A.B.C的版本号格式)的定义,「不向下兼容的变化」需要在主版本(即A)中体现。

所以「移除forwardRef」就和其他Breaking Change(比如移除propTypesdefaultProps)一起被包含在v19中。

促使官方最终决定移除forwardRef的,除了来自社区的各种抱怨,还有个重要的原因 —— ref最初的语义(对实例的引用)是在类组件盛行的时代产生的。随着Hooks的崛起,函数组件的重要性远超类组件。

既然类组件终将消失在历史中,那严守ref的语义就没意义了。

「不再执着于ref语义」也带来了另一个源码层面的变化。

如果你细心观察会发现,对于React Element与类组件,传递进来的ref会被「自动赋予对实例的引用」,而传递给forwardRefref,需要开发者手动决定怎么用:

// domRef.current 自动获得 HTMLInputElement <input ref={domRef} /> // instanceRef.current 自动获得 SomeClassComponent实例 <SomeClassComponent ref={instanceRef} /> forwardRef((props, ref) => { // 开发者自己决定将 ref.current 赋值给谁 // ... }); 

这种区别导致在类组件中,如果用解构对象的方式操作props,很可能意外将ref传递下去并自动获得实例引用。

比如下面代码中,如果otherProps中包含ref,就会被传递给SomeClassComponent并获得SomeClassComponent实例的引用:

let {myCustomProp, ...otherProps} = props; doSomething(myCustomProp); return <SomeClassComponent {...otherProps} />; 

所以,refkey作为JSX中两个特殊字段会被特殊处理,而其他props会被直接透传。

鉴于:

  1. 未来forwardRef被废弃
  2. 类组件被冷落
  3. 函数组件不会自动决定ref的赋值

未来ref也将变成一个「可以被透传的普通prop」

总结

本文详细解释了forwardRef

  1. 产生的背景、作用
  2. 存在的问题
  3. React核心团队对此的思考

往深了说,forwardRef从出现到消失,反映的其实是一个权力问题 —— 类组件失势,导致失去对ref语义的定义权。

失去底层语义的定义权,具体的实现(forwardRef)自然就不复存在了。

参考资料

[1]

RFC #107: https://github.com/reactjs/rfcs/pull/107#issuecomment-

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/179595.html

(0)
上一篇 2025-05-29 07:10
下一篇 2025-05-29 07:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信