React和Vue一样,不建议操作DOM,但是如果我们在某个场景必须操作DOM,那么也可以使用refs来实现。
refs是通过React.createRef()来创建的,通过使用ref属性来对应某个元素。一般在constructor中赋值给某个实例属性来达到多次复用的作用
class App extends React.Component{
render(){
return <MyComponent />
}
}
class MyComponent extends React.Component{
constructor(props){
super(props);
// 创建ref
this.myRef=React.createRef();
// 绑定方法
this.btnClick=this.btnClick.bind(this);
}
btnClick(){
// 打印ref对应的元素
// 元素被放在ref的current属性中
console.log(this.myRef.current);
}
render(){
return <button ref={
this.myRef} onClick={
this.btnClick}>123</button>
}
}
当refs被传递给render中的元素的时候,可以通过current属性来访问,如同上面的代码一样
btnClick(){
// 打印ref对应的元素
// 元素被放在ref的current属性中
console.log(this.myRef.current);
}
ref的current属性根据其放在不同的标签上有所不同
上面的代码就是第一种情况,对于第二种情况,修改上面的代码
class App extends React.Component{
render(){
return <MyComponent />
}
}
class MyComponent extends React.Component{
constructor(props){
super(props);
// 创建ref
this.myRef=React.createRef();
// 绑定方法
this.btnClick=this.btnClick.bind(this);
}
btnClick(){
// 打印ref对应的元素
// 元素被放在ref的current属性中
console.log(this.myRef.current);
}
render(){
return <div>
<button onClick={
this.btnClick}>123</button>
{
/* ref指向自定义组件 */}
<ChildComponent ref={
this.myRef} />
</div>
}
}
class ChildComponent extends React.Component{
render(){
return <div>111</div>
}
}
点击后发现和刚才结果不同
打印出来的变成了自定义组件的实例
因为ref的current指向的是组件的实例,和函数组件没有实例,所以无法将ref用在函数组件上
将刚才的自定义组件ChildComponent改成下面这个函数组件
function ChildComponent(props){
return <div>111</div>
}
控制台会出现报错如下
但是我们还是可以函数组件内部使用ref的
function ChildComponent(props){
let textRef=React.createRef();
function consoleRef(){
console.log(textRef.current);
}
return <div>
<p ref={
textRef}>123</p>
<button onClick={
consoleRef}>btn</button>
</div>
}
这里能用是理所当然的,这和是函数组件或者是class组件没有关系,ref是指向html元素的。
通过在子组件上面使用ref属性指向React.createRef()创建的refs来达到将DOM从子组件传递到父组件的目的
render(){
return <div>
<button onClick={
this.btnClick}>123</button>
{
/* ref指向自定义组件 */}
<ChildComponent ref={
this.myRef} />
</div>
}
然而,这种方法并非理想的解决方法,我们最终只能得到子组件的实例,而非DOM,更何况上面也说了,函数组件无法使用该方法。
refs转发通过React.forwardRef来创建一个子组件,并获取父组件的ref,将内部的元素中的ref属性指向传递来的ref,达到将DOM传递到父组件的目的
function ChildComponent(props){
let textRef=React.createRef();
function consoleRef(){
console.log(textRef.current);
}
return <div>
{
/* 获取子组件中的refs */}
<RefBtn ref={
textRef} />
<button onClick={
consoleRef}>clickbtn</button>
</div>
}
const RefBtn = React.forwardRef((props,ref)=>{
// ref属性指向父组件传下来的ref
return <button ref={
ref}>
refBtn
</button>
})
点击clickBtn,会打印出button的DOM
React.forwardRef生成的组件在react-devtools中被当成一个组件
refs回调是另一种设置refs的方法,不同于直接设置值,refs回调是通过传递一个方法,在方法中完成对refs的赋值,与之前不同的是,refs回调可以直接将元素赋值给refs,这样就不用多用一步去调用current属性了
class MyComponent extends React.Component{
constructor(props){
super(props);
// 创建ref
this.myRef=null;
this.setMyRef=element=>{
this.myRef=element;
}
// 绑定方法
this.btnClick=this.btnClick.bind(this);
}
btnClick(){
// 打印ref对应的元素
// 打印的不再是current属性
if(this.myRef)console.log(this.myRef);
}
componentDidMount(){
this.btnClick()
}
render(){
return <div>
<button onClick={
this.btnClick}>123</button>
<div ref={
this.setMyRef}>div</div>
</div>
}
}