Ref属性用来获取DOM元素的节点
和获取子组件的实例
。
Ref的更新迭代:
1、String Ref
String Ref
是个过时的API。因为String类型的Ref存在一些问题,将在未来的某个版本中被遗弃,不建议使用。
使用方式:this.refs.XXX
获取DOM元素节点:
import React, { Component } from 'react';
class App extends Component {
componentDidMount() {
console.log('this.refs.XXX');
console.log(this.refs.h1Ref);
}
render() {
return <h1 ref='h1Ref'>Hello World!</h1>
}
}
export default App;
打印结果:
获取子组件的实例:
Ref获取子组件的实例后,可以调用子组件上的方法。
import React, { Component } from 'react';
class App extends Component {
componentDidMount() {
console.log(this.refs.childRef);
this.refs.childRef.handleLog(); // Child Component }
render() {
return (
<div>
<h1>Hello World!</h1>
<Child ref='childRef' count='1' />
</div>
)
}
}
class Child extends Component {
handleLog = () => {
console.log('Child Component');
}
render() {
const { count } = this.props;
return <h2>count: { count }</h2>
}
}
export default App;
打印结果:
2、Callback Ref
Callback Ref
能助你更精细地控制何时 refs 被设置和解除,传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数。React 将在组件挂载时,会调用 ref
回调函数并传入 DOM 元素,当卸载时调用它并传入 null
。在 componentDidMount
或 componentDidUpdate
触发前,React 会保证 refs 一定是最新的。
使用方式:ref={element => (this.eleRef = element)}
获取DOM元素节点:
import React, { Component } from "react";
class App extends Component {
componentDidMount() {
console.log("Callback Ref");
console.log(this.h1Ref);
}
render() {
return (
<div>
<h1 ref={element => (this.h1Ref = element)}>Hello World!</h1>
</div>
);
}
}
export default App;
打印结果:
获取子组件实例:
import React, { Component } from "react";
class App extends Component {
componentDidMount() {
console.log("Callback Ref");
console.log(this.childRef);
this.childRef.handleLog();
}
render() {
return (
<div>
<h1>Hello World!</h1>
<Child ref={component => (this.childRef = component)} count="1" />
</div>
);
}
}
class Child extends Component {
handleLog = () => {
console.log("Child Component");
};
render() {
const { props } = this;
return <h1>count: {props.count}</h1>;
}
}
export default App;
打印结果:
3、Create Ref
React 16.3 发布React.createRef()
,并且是现在类组件中推荐使用的。
Refs 是使用 React.createRef()
创建的,并通过 ref
属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。
当 ref 被传递给 render
中的元素时,对该节点的引用可以在 ref 的 current
属性中被访问。
React 会在组件挂载时给 current
属性传入 DOM 元素,并在组件卸载时传入 null
值。ref
会在 componentDidMount
或 componentDidUpdate
生命周期钩子触发前更新。
获取DOM元素节点:
import React, { Component, createRef} from "react";
class App extends Component {
constructor(props) {
super(props);
this.h1Ref = createRef();
}
componentDidMount() {
console.log("React.createRef()");
console.log(this.h1Ref.current);
}
render() {
return <h1 ref={this.h1Ref}>Hello World!</h1>;
}
}
export default App;
打印结果:
获取子组件的实例:
import React, { Component, createRef } from "react";
class App extends Component {
constructor(props) {
super(props);
this.childRef = createRef();
}
componentDidMount() {
console.log("React.createRef()");
console.log(this.childRef.current);
this.childRef.current.handleLog();
}
render() {
return (
<div>
<h1>Hello World!</h1>
<Child ref={this.childRef} count="1" />
</div>
);
}
}
class Child extends Component {
handleLog = () => {
console.log("Child Component");
};
render() {
const { props } = this;
return <h1>count: {props.count}</h1>;
}
}
export default App;
打印结果:
4、useRef
在函数组件中使用String Ref、Callback Ref、Create Ref
会抛出以下错误❌:
Uncaught Invariant Violation: Function components cannot have refs. Did you mean to use React.forwardRef()?
是因为函数组件没有实例,所以函数组件中无法使用String Ref、Callback Ref、Create Ref
,取而代之的是useRef
。
useRef的作用:
- 获取DOM元素的节点
- 获取子组件的实例
- 渲染周期之间共享数据的存储(state不能存储跨渲染周期的数据,因为state的保存会触发组件重渲染)
获取DOM元素的节点
import React, { useEffect, useRef } from 'react';
function App() {
const h1Ref = useRef();
useEffect(() => {
console.log('useRef')
console.log(h1Ref.current)
}, [])
return <h1 ref={h1Ref}>Hello World!</h1>
}
export default App;
打印的结果:
获取子组件的实例:
注: 因为函数组件没有实例,如果想用ref获取子组件的实例,子组件组要写成类组件。
import React, { Component, useEffect, useRef } from 'react';
function App() {
const childRef = useRef();
useEffect(() => {
console.log('useRef')
console.log(childRef.current)
childRef.current.handleLog();
}, [])
return (
<div>
<h1>Hello World!</h1>
<Child ref={childRef} count="1"/>
</div>
)
}
// 因为函数组件没有实例,如果想用ref获取子组件的实例,子组件组要写成类组件
class Child extends Component {
handleLog = () => {
console.log('Child Component');
}
render() {
const { count } = this.props;
return <h2>count: { count }</h2>
}
}
export default App;
打印结果:
渲染周期之间共享数据的存储
类组件中,state不能存储跨渲染周期的组件,因为state的参数每一次保存都会触发组件的重渲染。
场景:声明一个参数为count = 0 ,组件初始化时count每秒钟加1,直到count > 5时停止增加,清除定时器。
把定时器设置成全局变量使用useRef挂载到current上
import React, { useState, useEffect, useRef } from "react";
function App() {
const [count, setCount] = useState(0);
// 把定时器设置成全局变量使用useRef挂载到current上
const timer = useRef();
// 首次加载useEffect方法执行一次设置定时器
useEffect(() => {
timer.current = setInterval(() => {
setCount(count => count + 1);
}, 1000);
}, []);
// count每次更新都会执行这个副作用,当count > 5时,清除定时器
useEffect(() => {
if (count > 5) {
clearInterval(timer.current);
}
});
return <h1>count: {count}</h1>;
}
export default App;