作为一名初学者, 我遇到了一个小问题, 父组件如何获取子组件的属性和方法呢?
如下图所示, div是input和button的父组件, 我想要实现的功能特别简单, 那就是在点击button按钮的时候, 可以打印出input输入框内的内容

该过程可以简单归结为下:
- 在
input内输入内容 - 点击
button调用父组件div的方法 div从input内取到内容,然后打印出来
第 1,2 步比较简单,我们使用props向button传递一个回调函数即可
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有三种方式:
- 使用字符串形式,及
ref="string", 但是该方式已经被弃用 - 使用一个回调函数
- 使用
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.info的current属性中! 所以, 修改代码如下:
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的性能也更好)
如下图所示, 我们在input和button下方, 新建一个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(), 在官方文档中, 是这么介绍它的:
forwardReflets your component expose a DOM node to parent component with aref
可以知道, 这个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后, 可以发现, 此时, 返回的是Child中div这个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 的是一个对象
})
文章详细介绍了在React中,父组件如何通过ref获取子组件的属性和调用子组件的方法。首先,通过props向子组件传递回调函数实现初步通信。接着,讲解了ref的概念,包括在类组件中使用回调函数和React.createRef()的方式获取子组件属性。在函数组件中,使用useRef()替代。最后,通过React.forwardRef()和useImperativeHandle()实现了父组件调用子组件方法的功能。
1194

被折叠的 条评论
为什么被折叠?



