用处
修改子组件、或获取组件的属性值
在典型的React数据流中,
props是父组件和子组件交互的唯一方式,要修改一个子组件,你需要使用新的props来重新渲染它。但是在某些情况下,你需要在典型的数据流之外强制修改子组件、或获取组件的属性值。被修改的子组件可能是一个React组件的实例,也可能是一个原生DOM元素。
Ref的三种创建方式
-
React.createRef()在react的16.3及以后的版本中,可以在实例的构造函数中使用
React.createRef()方法来创建ref, 并将其赋值给实例对象的自定义属性,以便于在整个组件中都可以使用, 然后再将其附加给原生HTML元素或Class类组件的ref属性上。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。current 属性值因附加在的节点类型不同而有所不同:
当ref附加在原生HTML元素上时, current属性值为该原生DOM对象;
当ref附加在Class类组件上时, current属性值为该组件的实例对象;
ref不能使用在函数组件上,因为函数组件没有实例对象
class Component extends React.Component {
constructor(props) {
super(props);
this.domRef = React.createRef();
this.classRef = React.createRef();
}
render() {
console.log("RefComponent", this.domRef, this.classRef)
return <div>
<div ref={this.domRef}> <div/>
<MyComponent ref={this.classRef} />
<div/>;
}
}
React在组件挂载时给current属性传入参数对象,在组件卸载时current属性传入null,在componentDidMount 和 componentDidUpdate触法前更新
- 回调函数方式
React 也支持另一种设置 refs 的方式,称为“回调 refs”。它能助你更精细地控制何时 refs 被设置和解除。
不同于传递createRef()创建的ref属性,“回调 refs”传递一个函数,并接受组件的实例对象或原生HTML DOM对象作为参数,并将该参数赋值给组件实例对象的自定义属性,以便于在整个组件中都可以使用。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.setMyRef = null;
}
render() {
return < div ref={ (el) => this.setMyRef = el } />;
}
}
React在组件挂载时会调用refs回调函数并传入参数对象,在组件卸载时再次调用回调函数传入null,在componentDidMount 和 componentDidUpdate触法前更新;
-
字符串方式
直接设置ref属性为一个字符串, 通过this.refs获取
const element = <div ref="myRef">string ref</div>
const node = this.refs.myRef
string 类型的 refs 存在一些问题, 该用法已过时
-
需要React去保持跟踪当前渲染的组件,有点拖累性能
-
当使用
render callback模式渲染子组件时, 为子组件设置ref在父组件中获取不到
class MyComponent extends Component {
renderRow = () => {
// 字符串设置的ref会被绑定到子组件DataTable上而不是父组件MyComponent
return <input ref="input" />;
}
render() {
return <DataTable renderRow={this.renderRow} />
}
}
//打印发现虽然在父组件中设置的ref, 但却不是绑定在父组件中,而是绑定在子组件中的refs中,
//如果通过回调函数设置,就可以避免该问题,
注意点
我们说不可以在函数组件上使用ref, 因为函数组件没有实例。如果非要在函数组件上使用ref,有两个办法:
-
将函数组件转为class类组件
-
使用forwardRef()生成函数组件
const FancyButton = React.forwardRef((props, ref) => { return <button ref={ref} >{props.children}</button> }) // 将ref作为React.forwardRef()函数的第二个参数传入,这样就可以在父组件中暴露子组件中的DOM ref
内联式的回调函数写法,在组件更新时会执行调用两次, 因为组件在更新后会生成新的实例对象(this),ref需要先清空旧值时调用一次,再赋值新值时又调用一次。