乘着睡觉前再来一篇关于函数式组件的 Ref 使用。
学过vue2的人都知道,this.$refs和组件里的 ref属性,
还有vue3的useRef绑定Dom。
<template>
<div>
<input type="text" ref="inputRef" />
<button @click="lookInputRef">click me</button>
</div>
</template>
<script>
export default {
methods: {
lookInputRef() {
console.log(this.$refs.inputRef);
},
},
};
</script>
<template>
<div>
<input type="text" :ref="inputRef" />
<button @click="lookInputRef">click me</button>
</div>
</template>
<script setup>
import {useRef} from "vue"
const inputRef = useRef(null)
function lookInputRef() {
console.log(inputRef.value);
}
</script>
以上就是vue关于绑定Dom元素的写法,那么react是怎么写的呢?
import React, { useRef, useImperativeHandle, forwardRef, createRef } from "react";
function TestUseRef() {
/*
useRef() 返回一个获取dom的值,把这个值放到需要获取的dom上,用ref绑定。
const inputRef = useRef()
<input type="text" ref={inputRef}/>
这样以来 inputRef 就会被赋予一个 current 值,这个值就是 input 的实例对象
*/
const inputRef = useRef<HTMLInputElement | null>(null)
const lookInputRef = () => {
console.log(inputRef.current);
}
return (
<div>
<input type="text" ref={inputRef}/>
<button onClick={lookInputRef}>
看看值
</button>
</div>
)
}
export default TestUseRef;
点击按钮之后就打印了这个实例对象。
那么要获取子组件的dom是怎么样呢。
function TestUseRef() {
/*
useRef() 返回一个获取dom的值,把这个值放到需要获取的dom上,用ref绑定。
const inputRef = useRef()
<input type="text" ref={inputRef}/>
这样以来 inputRef 就会被赋予一个 current 值,这个值就是 input 的实例对象
*/
const inputRef = useRef<HTMLInputElement | null>(null);
const inputRefFoo = useRef<HTMLInputElement | null>(null);
const lookInputRef = () => {
console.log(inputRef.current);
console.log(inputRefFoo.current);
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={lookInputRef}>看看值</button>
<Child ref={inputRefFoo}></Child>
</div>
);
}
const Child = forwardRef((props: any, inputRef: any) => {
// 这里被forwardRef包裹后 第二个参数 是父组件传入的 ref,所以可以不用创建ref了
// const inputRef = useRef<HTMLInputElement | null>(null);
const handleInputFocus = () => {
console.log(inputRef);
inputRef.current?.focus();
};
return (
<div>
<input type="text" ref={inputRef} placeholder="input me!" />
<button onClick={handleInputFocus}>click to do input focus</button>
</div>
);
});
export default TestUseRef;
用forwardRef把子组件包裹起来,这样子组件的第二个参数就变成了一个ref值,
代表父组件通过ref属性传递过来的 inputRefFoo
那如果我只想让父组件获得 handleInputFocus 这个函数而不是这个 input实例对象呢。
可以用 useImperativeHandle 将内部函数返回出去。
const Child = forwardRef((props: any, Ref: any) => {
//需要使用 useImperativeHandle 将函数抛出, 父组件才能使用 ref 中的方法
useImperativeHandle(Ref, () => ({
handleInputFocus: () => handleInputFocus,
}));
const inputRef = useRef<HTMLInputElement | null>(null);
const handleInputFocus = () => {
console.log(inputRef);
// inputRef.current?.focus();
};
return (
<div>
<input type="text" ref={inputRef} placeholder="input me!" />
<button onClick={handleInputFocus}>click to do input focus</button>
</div>
);
});
inputRef在子组件内部自己创建,然后用useImperativeHandle将函数 handleInputFocus 返回出去,
这样就可以让父组件使用子组件内部的方法了。
以上总结一下,
useRef 创建一个获取dom的对象,这个对象被赋予了一个属性为 current 的值,这个值就是绑定的Dom实例。
父组件要使用子组件中被创建的dom实例就得用 forwardRef 方法把子组件包起来,这个子组件的第二个参数就变成了父组件
传入的ref实例,这样父组件就能在自己的函数里使用子组件的Dom实例了。
当然子组件也可以自己管理自己的实例,用 useImperativeHandle 把父组件传入的Ref放进第一个参数里,
然后第二个参数里吧自身的函数返回出去,这样父组件就只获得子组件的方法而不是实例了。