简单手写实现函数组件的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
打开自己文件的路径
正常运行,✌️~~~