【React hooks】 useRef、forwardRef、useImperativeHandle

136 阅读2分钟

摘要

在类组件中,父组件获取子组件实例是通过ref实现的,比如以下代码

import React, { Component } from "react";
import Son from "./son";

export default class Parent extends Component {
    sonRef = React.createRef();
    fatherSetCount = () => {
        this.sonRef.current.setCount();
    };
    render() {
        return (
            <div>
                <Son ref={this.sonRef} />
                <br />
                <button onClick={() => this.sonRef.current.setCount()}>
                 fatherSetCount
                </button>
            </div>
        );
    }
}


import React, { Component } from "react";

export default class Son extends Component {
    state = {
        count: 0,
    };
    setCount = () => {
        this.setState({
                count: ++this.state.count,
        });
    };
    render() {
        return (
            <div>
                <p>{this.state.count}</p>
                <button onClick={this.setCount}>setCount</button>
            </div>
        );
    }
}

那么在函数组件中如何获取子组件实例呢,在介绍方法之前首先介绍几个api

useRef

  • useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue
  • useRef 返回的 ref 对象在组件的整个生命周期内持续存在
  • ref对象地址从头到尾都不会改变
  • useRef变化不会主动使页面渲染

forwardRef

我们在函数组件里直接写ref时,控制台会报警告

image.png 从上图知道,函数组件要使用ref属性,需要使用forwardRef,转发ref,比如以下代码,父组件传递子组件ref属性,子组件通过forwardRef接受并传递给input,在父组件中通过sonRef.current.value即可获取input的值

import React, { useRef } from 'react'
import Son from "./son";

export default function Parent() {
    const sonRef = useRef();
    const logInput = () => {
            console.log(sonRef.current.value);
    }
    return (
        <div>
                <Son ref={sonRef}/>
                <button onClick={logInput}>logInput</button>
        </div>
    )
}
import React, { forwardRef } from 'react'

const SonComponent = forwardRef((props,ref) => {
    return (
        <div>
                <input type="text" ref={ref} />
        </div>
    )
})

export default SonComponent;

那么问题来了,就算这样父组件还是拿不到子组件的方法,这时候就需要搭配useImperativeHandle来实现

useImperativeHandle

useImperativeHandle可以让你在使用ref时自定义暴露给父组件的实例值,简单来说就是可以选择将啥暴露给父组件,而不是像类组件中使用ref那样全部暴露,所以,useImperativeHandle用来减少暴露给父组件获取的DOM元素属性, 只暴露给父组件需要用到的DOM方法

参数:

  • 参数1: 父组件传递的ref属性
  • 参数2: 返回一个对象, 以供给父组件中通过ref.current调用该对象中的方法

🌰:函数组件父组件调用子组件函数实例

import React from "react";
import Child from "./Child";
import { useRef } from "react";

export default function Demo() {
  const textInput = useRef(null);
  const childRef = useRef(null);
  function onButtonClick() {
    textInput.current.focus();
  }
  function childClick() {
    console.log(childRef.current)
    childRef.current.childFun();
  }
  return (
    <div>
      <input type="text" ref={textInput} />
      <button onClick={onButtonClick}>Focus the input</button>
      <Child ref={childRef}/>
      <button onClick={childClick}>childClick</button>
    </div>
  );
}
import React from "react";
import { forwardRef, useImperativeHandle } from "react";

const Child = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    childFun,
  }));
  const childFun = () => {
    console.log("999");
  };
  return <div>Child</div>;
});

export default Child;