ref通常被用来获取DOM元素或组件实例
string类型的ref
<input type='text' ref='input' />
......
console.log(this.$refs.input) // <input type='text' />
$refs 是所有注册过的ref的一个集合
在react中,一般使用function类型的ref,上面的这种string类型的ref将会被废弃,因为它无法直接获取this的指向,并且当使用render回调函数的开发模式,获得ref的组件实例可能与预期不同。
function类型的ref
<WebView ref={ref => this.webview = ref } // 将该组件赋值给webview />
console.log(this.webview)
通过React.createRef()创建ref:
this.webview = React.createRef();
// 引用
<WebView ref={this.webview} />
// 获取当前节点
console.warn(this.webview.current)
React.createRef()创建的ref对象仅仅包含current属性,表示获取的DOM元素或组件实例
useRef
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
即每次返回的ref对象都是一开始传入的
const refContainer = useRef(initialValue);
ref的生命周期
在React中,HostComponent、ClassComponent、ForwardRef可以赋值ref属性。ForwardRef只是将ref作为第二个参数传递下去,没有别的特殊处理。
Ref属性在ref不同的生命周期会被执行 ( fuction类型 ) 或赋值 ( {current: any}对象类型 )
ref的生命周期与react的渲染一样,可以分为 两个阶段
render阶段:
为含有ref属性的Component对应fiber添加Ref effectTag,fiber类型为HostComponent、ClassComponent、ScopeComponent
- 对于mount,workInProgress.ref !== null,即组件首次render时存在ref属性
- 对于update,current.ref !== workInProgress.ref,即组件更新时ref属性改变
commit阶段:
为包含Ref effectTag的fiber执行对应操作
- 移除之前的ref
function commitDetachRef(current: Fiber) {
const currentRef = current.ref;
if (currentRef !== null) {
if (typeof currentRef === 'function') {
// function类型ref,调用他,传参为null
currentRef(null);
} else {
// 对象类型ref,current赋值为null
currentRef.current = null;
}
}
}
- 更新ref
function commitAttachRef(finishedWork: Fiber) {
// finishedWork为含有Ref effectTag的fiber
const ref = finishedWork.ref;
// 含有ref prop,这里是作为数据结构
if (ref !== null) {
// 获取ref属性对应的Component实例
const instance = finishedWork.stateNode;
let instanceToUse;
switch (finishedWork.tag) {
case HostComponent:
// 对于HostComponent,实例为对应DOM节点
instanceToUse = getPublicInstance(instance);
break;
default:
// 其他类型实例为fiber.stateNode
instanceToUse = instance;
}
// 赋值ref
if (typeof ref === 'function') {
ref(instanceToUse);
} else {
ref.current = instanceToUse;
}
}
}