Refs 提供了一种可以便捷访问DOM元素和React组件实例的方式。可以通过Refs强制执行一些实例方法。例如:控制input标签的焦点管理、控制视频组件(<video/>标签)的播放管理。
Refs的三种创建方式
- String 类型的Refs. (已过时,不建议使用)
在标签处为
ref赋值。如下
<input ref="numberInput"></input>
然后在组件中可以使用this访问:
this.refs.numberInput
- 创建Refs变量(在React16.3版本中引进)
在构建组件的时候,通过
React.createRef()创建ref变量,然后在附加到实例标签。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
然后在组件中可以使用如下方式获取
const node = this.myRef.current;
- 回调方式创建Refs. React会在组件挂载的时候调用Ref回调函数并传入DOM元素。
import React from "react";
export class CustomInput extends React.Component {
constructor(props) {
super();
this.myInputRef = null;
this.setMyInputRef = element => {
this.myInputRef = element;
};
}
render() {
return (<div>
<input ref={this.setMyInputRef} />
</div>
);
}
}
Ref回调函数会在更新过程中执行两次,第一次由于组件没有挂载,会传入null。第二次会传入真正的DOM元素。另外Ref回调函数和其他组件事件监听函数例如:onChange、onMouseMove 等一样,最好提前绑定this. 不然会重复创建函数变量。
Refs的转发
在实际开发中经常遇到访问已封装好组件的内部组件(子组件)或DOM元素,例如访问高阶组件的包裹组件。
如果直接在高阶组件上使用ref,引用到的事高阶组件本身的实例,显然这不是我们期待的。不过有两种方式可以解决这个问题。下面通过CustomTextInput 组件自动获焦的实例阐述这两种方法。
- Ref回调当做组件属性传入高阶组件
import React from "react";
import { CustomTextInputHOC } from "./CustomTextInputHOC";
class CustomTextInput extends React.Component {
constructor(props) {
super();
this.state = {
text: "nihao"
};
this.inputRef = React.createRef();
}
onTextChange = event => {
event.persist();
this.setState({
text: event.target.value
});
};
onFocus = () => {
this.inputRef.current.focus();
};
render() {
return (
<div>
<input ref={this.inputRef} onChange={this.onTextChange} />
<div>{this.state.text}</div>
</div>
);
}
}
export default CustomTextInputHOC(CustomTextInput);
CustomTextInputHOC组件定义如下:
import React from "react";
export function CustomTextInputHOC(WrapComp) {
return class extends React.Component {
render() {
const { createRefFunc, name } = this.props;
return <WrapComp ref={createRefFunc} name={name} />;
}
};
}
在App里挂载CustomTextInput 组件:
export default function App() {
let inputRef = null;
const createRef = ref => {
inputRef = ref;
};
useEffect(() => {
console.log(inputRef);
inputRef.onFocus();
});
return <CustomTextInput createRefFunc={createRef} />;
}
- 通过
React.forwardRef劫持ref变量,然后向下传递。
CustomTextInput 组件没有变化和上面实现的一致
CustomTextInputHOC 组件的实现如下:
import React from "react";
export function CustomTextInputHOC(WrapComp) {
class MyComp extends React.Component {
render() {
const { forwardRef, name } = this.props;
return <WrapComp ref={forwardRef} name={name} />;
}
}
return React.forwardRef((props, ref) => {
return <MyComp {...props} forwardRef={ref} />;
});
}
在根节点挂载CustomTextInput组件
export default function App() {
let inputRef = React.createRef();
useEffect(()=> {
inputRef.current.onFocus();
})
return <CustomTextInput ref={inputRef} />;
}