10.引入forwardRef的底层逻辑
上一小节我们实现了类组件的ref相关代码,本小节我们来探索如何为函数式组件引入ref的相关能力。大家想一想,类组件、函数组件都是组件,为什么函数组件不能像类组件那样实现ref的相关功能,还要在这里单独用一小节来进行探索。
我们在实现类组件的ref相关功能的时候,之所以能将ref和类组件生成的DOM进行关联,是因为我们当时通过ref && (ref.current = instance);先将类组件的实例和ref进行关联,然后通过调用实例的方法间接操作。
在前面分析类组件的ref实现原理的小节,我们已经知道要让ref发挥作用可以通过以下3个步骤完成:
- 初始化:
this.myRef = React.createRef();相当于 this.myRef = - 传递ref引用:
ref= { this.myRef },这里有个隐藏的动作,this.myRef.current = xxx dom - 取值:
const element = this.myRef.current;
如果我们企图在函数组件中,模仿类组件的这三个步骤来实现ref的相关功能是无法成功的,比如官方文档上提供的下面的代码:
// https://reactjs.org/docs/refs-and-the-dom.html
function MyFunctionComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// This will *not* work!
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}为什么不能成功呢?
https://reactjs.org/docs/refs-and-the-dom.html 官方文档提到过:By default, you may not use the ref attribute on function components because they don’t have instances
官方文档说是因为函数组件没有实例,这个解释很正确,但对于很多同学来讲可能会感觉不知所云。我们先忽略这个解释,跟着我的思路往下走。
我们在本章前面介绍过,函数组件本质上就是一个函数,只不过这个函数的返回值是一个虚拟DOM。而这段代码中,我们在使用函数组件的时候,只是将this.textInput和MyFunctionComponent进行关联,但并没有将this.textInput和MyFunctionComponent的返回值进行关联。问题产生的原因已经很清晰,那解决方案也就很清晰了,那就是建立函数组件的ref值和函数组件对应函数的返回值之间的关联。那现在面临的问题就是怎么进行关联,能自然联想到的方式就是将ref作为函数组件的参数传入,并在返回值中进行手动使用,比如:
function MyFunctionComponent(props, functionRef) {
return <input ref={functionRef}/>;
}将react-dom.js中的getDomByFunctionComponent进行改造:
function getDomByFunctionComponent(vNode) {
let { type, props, ref } = vNode;
let renderVNode = type(props, ref);
if (!renderVNode) return null;
return createDOM(renderVNode);
}但是这种方案有个弊端,就是每个函数组件的返回值都需要我们手动进行ref的关联,给日常开发带来了困扰,这种方案当然是不可行的,所以我们需要改进,将前面想到的手动关联的方式转化成自动关联。所以React提供了一个单独的api来实现这个关联的过程,这个api就是forwardRef,至于forwardRef具体如何实现我们会在下一小节进行介绍。。