父组件如何获取子组件的属性和方法-使用Ref

作为一名初学者, 我遇到了一个小问题, 父组件如何获取子组件的属性和方法呢?

如下图所示, divinputbutton的父组件, 我想要实现的功能特别简单, 那就是在点击button按钮的时候, 可以打印出input输入框内的内容

在这里插入图片描述

该过程可以简单归结为下:

  1. input内输入内容
  2. 点击button调用父组件div的方法
  3. divinput内取到内容,然后打印出来

第 1,2 步比较简单,我们使用propsbutton传递一个回调函数即可

function App() {

  const showMess = () => {
    console.log("click button success");
  }

  return (
    <div>
      <input placeholder='Please input message' className='input'/>
      <br />
      <br />
      <button onClick={showMess} className='button'>Click to show message</button>
    </div>
  );
}

export default App;

在这里插入图片描述

如图所示, 当我们点击button后, 控制台成功打印出了消息, 表示我们 1,2 步已经完成. 但是, div这个父组件要如何获取input内输入的内容呢? 这就需要用到**ref**了

1. 什么是Ref

React中文文档中, 对于ref是这么解释的:

Refs 提供了一种方式, 允许我们访问DOM节点或在render方法中创建的React元素

而在最新的官方文档中,对于ref的定义如下:

When you want to a component to “remember” some information, but you don’t want that information to trigger new renders(触发新的渲染), you can use a ref

2. 如何使用Ref获取属性

最新的React文档中, 对于ref的使用, 已经全部更新为hook的形式, 但是, 对类组件中ref的使用, 还是有必要进行学习的

2.1 类组件中Ref的使用

在类组件中,使用ref有三种方式:

  1. 使用字符串形式,及 ref="string", 但是该方式已经被弃用
  2. 使用一个回调函数
  3. 使用createRef

但在这之前, 我们首先将刚才的代码修改为一个类组件

  • AppClass.js:
import * as React from "react";
import './App.css'

export default class AppClass extends React.Component {

  constructor(props) {
    super(props);
    this.showMess = this.showMess.bind(this);
  }

  showMess () {
      console.log("click button success");
    }

  render() {
    return (
      <div>
        <input className='input'/>
        <br />
        <br />
        <button onClick={this.showMess} className="button">
          Click to show message
        </button>
      </div>
    );
  }
}
2.1.1 使用回调函数

使用回调函数的形式如下:

<input ref={currentNode => this.info = currentNode} className='input'/>

其中, 通过回调函数, 其实相当于将input这个节点和this.info进行了绑定, 之后, 就可以使用this.info来操作当前节点

让我们在button回调函数中添加一个方法

showMess () {
      console.log("click button success");
      console.log(this.info.alue);
}

从下图可以看到, 已经成功获取了

在这里插入图片描述

2.1.2 使用React.createRef()

使用React.createRef()的形式如下:

  constructor(props) {
    super(props);
    this.showMess = this.showMess.bind(this);
    this.info = React.createRef();
  }

  showMess () {
      console.log("click button success");
      console.log(this.info.value);
    }

但此时, 我们却发现, 控制台打印的并不是input内的值, 而是一个undefined

在这里插入图片描述

这是为什么呢? 让我们把this.info打印出来看一下

在这里插入图片描述

我们发现, input被放在了this.infocurrent属性中! 所以, 修改代码如下:

  constructor(props) {
    super(props);
    this.showMess = this.showMess.bind(this);
    this.info = React.createRef();
  }

  showMess () {
      console.log("click button success");
      // 注意这里修改为了 this.info.current.value
      console.log(this.info.current.value);
    }

再试一次, 可以成功打印我们需要的信息了(节省篇幅,不再放结果图片了)

2.2 函数组件中Ref的使用

HOOK为我们提供了useRef()这个钩子, 但是, 我们也应该知道, 函数组件中同样可以使用类组件中的两个方法(虽然不推荐)

2.2.1 使用回调函数
function App() {
  let info;
  const showMess = () => {
    console.log("click button success");
    console.log(info.value);
  }
  return (
    <div>
      <input ref={currentNode => info = currentNode} className='input'/>
      <br />
      <br />
      <button onClick={showMess} className='button'>Click to show message</button>
    </div>
  );
}
export default App;
2.2.2 使用React.createRef()
import React from 'react';
import './App.css';

function App() {
  let info = React.createRef(); // 不要忘记 import React
  const showMess = () => {
    console.log("click button success");
    console.log(info.current.value); // 记得这里要从 current 获取数据
  }
  return (
    <div>
      <input ref={info} className='input'/>
      <br />
      <br />
      <button onClick={showMess} className='button'>Click to show message</button>
    </div>
  );
}
export default App;
2.2.3 使用useRef()

useRef()React.createRef一样, 都是将DOM节点存储在current属性内

import { useRef } from 'react';
import './App.css';
function App() {
    
  let info = useRef(null);
  const showMess = () => {
    console.log('Clicked');
    console.log(info.current.value);
  }

  return (
    <div >
      <input ref={info} placeholder='Please input your message' className='input' />
      <br />
      <br />
      <button onClick={showMess} className='button'>Click to print message</button>
    </div>
  );
}
export default App;

3. 如何使用Ref调用子组件方法

上面, 我们已经成功地使用ref来获取子组件的属性了, 但是, 我们要如何调用子组件的属性呢? 其实, 和使用属性的方法差不多. 下面将通过useRef()这方式来调用子组件的方法. (函数组件不能使用ref, React.createRef不能使用, 除此之外, useRef的性能也更好)

如下图所示, 我们在inputbutton下方, 新建一个Child组件

在这里插入图片描述

Child组件中, 有一个func方法, 可以打印一句话

import React from 'react'
export default function Child() {

    const func = () => {
      console.log("This is Child's function");
    }
    

  return (
    <div style={{textAlign: 'center'}}>
        I am Child.
    </div>
  )
}

3.1 通过useRef()调用子组件方法

我们在Child这个子组件中, 添加ref, 并在App中使用child.current.func()调用其中的方法

function App() {

  let info = React.createRef();
  let child = useRef(null);

  const showMess = () => {
    console.log('Clicked');
    child.current.func();
  }


  return (
    <div >
      <input ref={info} placeholder='Please input your message' className='input' />
      <br />
      <br />
      <button onClick={showMess} className='button'>Click to print message</button>
      <br />
      <br />
      <Child ref={child} />
    </div>
  );
}

export default App;

但是当我们打开网页和控制台, 却发现如下报错: warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

在这里插入图片描述

这个报错在前面提示我们:函数组件不能被赋予refs, 而在最后提示我们: Did you mean to use React.forwardRef()?, 让我们去搜索一下这个React.forwardRef(), 在官方文档中, 是这么介绍它的:

forwardRef lets your component expose a DOM node to parent component with a ref

可以知道, 这个React.forwardRef()的作用, 就是让子组件暴露给父组件, 所以, 我们对子组件的代码进行修改

import React, { forwardRef, useImperativeHandle } from 'react'

const Child = forwardRef(function Child(props, ref) {
    
    const func = () => {
        console.log("This is child's function")
    }

    return (
        <div style={{ textAlign: 'center' }} ref={ref}>
            I am Child.
        </div>
    )
})
export default Child;

但是, 当我们在浏览器中打印child.current后, 可以发现, 此时, 返回的是Childdiv这个DOM元素

在这里插入图片描述

可以知道, 如果我们想要返回子组件中某一个DOM元素, 可以用上面的方法, 但我们现在是想调用子组件的某个方法, 此时这种方式就不行了, 需要用到另一个函数: useImperativeHandle()

import React, { forwardRef, useImperativeHandle } from 'react'

const Child = forwardRef(function Child(props, ref) {

    useImperativeHandle(ref, () => {
        return {
            func() {
                console.log("This is Child's function");
            }
        }
    })

    return (
        <div style={{ textAlign: 'center' }} >
            I am Child.
        </div>
    )
})

export default Child;

再次打开浏览器, 可以看到, 此时child.current已经返回的是func这个函数, 而该方法也已经成功执行

在这里插入图片描述

useImperativeHandle()函数的用法如下:

useImperative(ref, () => { // ref 即为子组件传递进的ref
    return {} // return 的是一个对象
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值