【React】useRef、useImperativeHandle


在这里插入图片描述

类组件中ref的使用

在类组件中,基于ref可以做的事情

1、赋值给一个标签,获取DOM元素
2、赋值给一个类子组件:获取子组件实例「可以基于实例调用子组件中的属性和方法」
3、赋值给一个函数子组件:报错「需要配合React.forwardRef实现ref转发,获取子组件中的某一个DOM元素」

ref的使用方法:

1、ref='box'

this.refs.box 获取(不推荐使用,严格模式报错)

2、ref=(x=>this.box= x)

this.box 获取

3、let box = React.creatdRef()创建一个ref对象

<h2 ref={
    
    this.box}>

通过this.box.current获取DOM元素

函数组件中useRef

useRef 可以用来创建一个跨渲染保持不变的引用,通常用来获取 DOM 元素或保存任何跨渲染保持不变的值。

基本语法:

const myRef = useRef(initialValue);

  • initialValueuseRef 创建的引用的初始值。
  • myRef是一个对象,具有 .current 属性,可以通过 .current 来访问或修改该引用的值。

函数组件中

1、基于ref = {函数}的方式(不推荐使用)

基于ref = {函数}的方式,可以把创建的DOM元素(或者子组件的实例)赋值给box变量

let box;
useEffect(()=>{
    
    
console.log(box)
},[])
return <div ref={
    
    x=>box=x}></div>

2、基于React.createRef创建ref对象来获取想要的内容

let box = React.createRef();
useEffect(()=>{
    
    
console.log(box.current)
},[])
return <div ref={
    
    box}></div>

3、useRef

函数组件中,可以基于 useRef Hook函数,创建ref对象(React.createRef创建ref对象,函数组件、类组件都可以用)

let box = useRef(null);
console.log(box)
useEffect(()=>{
    
    
console.log(box.current)
},[])
return <div ref={
    
    box}></div>

4、useRef 和 React.createRef的区别

useRef在每一次组件更新的时候(函数重新执行),再次执行useRef方法的时候,不会创建新的REF对象了,获取到的还是第一次创建的那个REF对象!!React.createRef在每一次组件更新的时候,都会创建一个全新的REF对象出来,比较浪费性能!!

而类组件只有在初始渲染会创建ref对象,组件更新时是render函数执行,不会再次重新创建ref对象

总结:在类组件中,创建REF对象,我们基于 React.createRef 处理;但是在函数组件中,为了保证性能,我们应该使用 useRef 处理!!

我们来测试下上述的结论是否正确:

let prev1, prev2;
const Demo = function Demo() {
    
    
    let [num, setNum] = useState(0);
    let box1 = useRef(null),
        box2 = React.createRef();
    if (!prev1) {
    
    
        // 第一次DEMO执行,把第一次创建的REF对象赋值给变量
        prev1 = box1;
        prev2 = box2;
    } else {
    
    
        // 第二次DEMO执行,我们验证一下,新创建的REF对象,和之前第一次创建的REF对象,是否一致?
        console.log(prev1 === box1); //true  useRef再每一次组件更新的时候(函数重新执行),再次执行useRef方法的时候,不会创建新的REF对象了,获取到的还是第一次创建的那个REF对象!!
        console.log(prev2 === box2); //false createRef在每一次组件更新的时候,都会创建一个全新的REF对象出来,比较浪费性能!!
        // 总结:在类组件中,创建REF对象,我们基于 React.createRef 处理;但是在函数组件中,为了保证性能,我们应该使用专属的 useRef 处理!!
    }
    useEffect(() => {
    
    
        console.log(box1.current);
        console.log(box2.current);
    });

    return <div className="demo">
        <span className="num" ref={
    
    box1}>{
    
    num}</span>
        <span className="num" ref={
    
    box2}>哈哈哈</span>
        <Button type="primary" size="small"
            onClick={
    
    () => {
    
    
                setNum(num + 1);
            }}>
            新增
        </Button>
    </div>;
};

5、获取子组件对象

父组件

const Demo = function Demo() {
    
    
    let x = useRef(null);
    useEffect(() => {
    
    
        console.log(x.current);
    }, []);

    return <div className="demo">
        <Child ref={
    
    x} />
    </div>;
};

子组件为类组件时

基于ref获取子组件的实例,这样基于实例,可以调用子组件内部,挂载到实例上的东西

class Child extends React.Component {
    
    
    state = {
    
     x: 1000 };
    render() {
    
    
        return <div className="child-box">
            {
    
    this.state.x}
        </div>;
    }
}

子组件为函数组件

基于React.forwardRef实现ref转发,目的:获取子组件内部的某个元素

const Child = React.forwardRef(function Child(props, ref) {
    
    
    // console.log(ref); //在DEMO中,调用Child的时候,传递的ref对象「x」
    return <div className="child-box">
        <span ref={
    
    ref}>哈哈哈</span>
    </div>;
}); 

useImperativeHandle

在函数子组件内部有自己的状态和方法时,如何实现:基于React.forwardRef实现ref转发的同时,获取函数子组件内部的状态或者方法呢?k可以使用useImperativeHandle

useImperativeHandle 允许你在使用 forwardRef 转发 ref 时自定义暴露给父组件的实例值。简单来说,它可以让你控制通过ref传递到父组件的对象和方法。

语法:
其依赖项dependencies和 useEffect 的依赖项用法一样

useImperativeHandle(ref, () => {
    
    
  return {
    
    
    methodName: () => {
    
    
      // 实现方法
    }
  };
}, [dependencies]);

-ref是父组件传递的 ref,通常与 forwardRef 一起使用。

  • 第二个参数是一个返回对象的函数,这个对象包含了你希望暴露给父组件的属性和方法。
    -dependencies可以是一个依赖数组,只有当这些依赖发生变化时,暴露的值才会更新。

父组件

调用子组件中的submit方法。

const Demo = function Demo() {
    
    
    let x = useRef(null);
    useEffect(() => {
    
    
         console.log(x.current,"x.current")
        console.log(x.current.submit());
    }, []);

    return <div className="demo">
        <Child ref={
    
    x} />
    </div>;
};

父组件打印结果如下:
在这里插入图片描述

子组件

const Child = React.forwardRef(function Child(props, ref) {
    
    
    let [text, setText] = useState('你好世界');
    const submit = () => {
    
     
    console.log('我是子组件中的submit方法')
   };

    useImperativeHandle(ref, () => {
    
    
       // ref 是父组件传递的 ref,通常与 forwardRef 一起使用。
        // 在这里返回的内容,都可以被父组件的REF对象获取到
        return {
    
    
            text,
            submit
        };
    });

    return <div className="child-box">
        <span>哈哈哈</span>
    </div>;
});