简单手写实现函数组件的Ref - forwardRef
继续学习写源码系列,本系列是学习系列。
- 简单手写实现 React 的 createElement 和 render,里面有准备工作,不再赘述。
- 简单手写实现 react 的函数组件
- 简单手写实现 react 的类组件
- 简单手写实现 React 类组件的 state 更新
- 简单手写实现 React 类组件的 state 批量更新
- 简单手写实现元素的 Ref
- 简单手写实现类组件的 Ref
本文的目标是,手写实现函数的Ref
,函数组件需要借助 forwardRef
。
TL;DR
- 函数组件用ref的话,需要另外裹下
forwardRef
forwardRef
其实就是将函数组件增加一个ref参数,然后解析的时候,传到组件里,从而进行赋值forwardRef
是一种新的type,需要增加新的解析方法。为了区分函数组件,将其加个type属性
准备工作
先将index.js
的点击事件略微修改
// import React from './source/react';
// import ReactDOM from './source/react-dom';
import React from 'react';
import ReactDOM from 'react-dom';
const TextInput = React.forwardRef((props, ref) => <input ref={ref} />);
class Form extends React.Component {
constructor(props) {
super(props);
this.textInputRef = React.createRef();
}
getFocus = () => {
console.log(this.textInputRef.current);
this.textInputRef.current.focus();
};
render() {
return (
<>
<TextInput ref={this.textInputRef} />
<button onClick={this.getFocus}>获得焦点</button>
</>
);
}
}
ReactDOM.render(<Form />, document.getElementById('root'));
点击获得焦点,输入框获得焦点:
分析 ref
<TextInput ref={this.textInputRef} />
其实本质是借助forwardRef
将this.textInputRef
赋值为TextInput
里input
的 DOM- 此处就是难点,本身函数组件并不好赋值 ref,这边将本身的函数组件,再次切片,从而将利用参数,将 ref 引用带到组件里,组件里自动赋值,从而外围拿到函数组件内部的 ref。
- 因为包裹了一层 forwardRef,所以这边多增加一个类型,只有被包裹的函数组件,才有获取 ref 值
实现
1.增加 forwardRef
本文为了区分普通函数组件,将forwardRef(fnCp)
称为转发的函数组件。
forwardRef 入参是函数,此函数其实就是组件函数,但多一个 ref 参数 forward 返回值返回该函数,但为了区分函数组件和转发的函数组件,所以额外加个 type。
// react.js
// fn是个函数,长得这样(props,ref)=>(<input ref={ref}/>),这里的ref就是例子的textInputRef
const forwardRef = (fn) => {
fn.type = 'isFunctionForward';
return fn;
};
解析转发的函数组件
type
,增加一种,转发的函数组件类型- 解析的时候,增加转发的函数组件类型,例子的
textInputRef
在这时候传过去的
// react-dom.js
// type增加
const getType = (vdom) => {
const isFn = isObject && typeof vdom.type === 'function';
if (isFn) {
if (vdom.type.isFunctionForward) {
return 'functionForward';
}
if (vdom.type.isClassComponent) {
return 'class';
}
return 'function';
}
}
// 3.1 {type:forward((props,ref)=>()),props:{ref:..}},props有ref,返回值才是vdom
// 和函数组件相比,就是多了ref,也是此处将ref传过去的
const handleFunctionForwardType = (vdom) => {
const { type: fn, props,ref } = vdom;
const vdomReturn = fn(props,ref);
return createElementDOM(vdomReturn);
};
const typeMap = new Map([
['text', handleTextType],
['element', handleElementType],
['function', handleFunctionType],
['functionForward', handleFunctionForwardType],
['class', handleClassType],
]);
老规矩,index.js
打开自己文件的路径
正常运行,✌️~~~
转载自:https://juejin.cn/post/7146089462307487781