React中Ref的使用(尽量少用)
ref是React提供的用来操纵React组件实例或者DOM元素的接口。表示为对组件真正实例的引用,其实就是ReactDOM.render()返回的组件实例。ref可以挂到任何元素上,可以挂到组件上也可以挂载到DOM元素上。
Class组件中使用ref
在React的Class组件时期,我们通过createRef创建ref。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
render() {
return <input type="text" ref={this.inputRef} />;
}
componentDidMount() {
this.inputRef.current.focus();
}
}
在这个例子里ref挂到了原生DOM元素,在这种情况下可以通过ref.current获取到这个DOM元素,并直接调用上面的方法。ref如果挂在到一个Class组件上,这样ref.current获取到的就是这个Class组件的实例。
函数式组件中使用ref
但是,ref不能挂到一个函数式组件(除非使用forwardRef),因为:ref回调函数会在组件被挂载之后将组件实例传递给函数,函数式组件没有实例。
在函数式组件中通过useRef创建ref。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
creactRef和useRef的区别
createRef 只能用在class组件中,useRef 只能用在函数式组件中。createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用。如果在函数式组件中使用createRef创建的ref,其值会随着函数式组件的重新执行而不断初始化。hooks不能用在class组件中,所以class组件只能使用createRef。
forwardRef
- 前面我们说到:ref不能挂到一个函数式组件(除非使用forwardRef)。
- forwardRef可以直接包裹一个函数式组件,被包裹的函数式组件会获得被分配给自己的ref(作为第二个参数)。
- 如果你直接将ref分配给没有被forwardRef包裹的函数式组件,React会在控制台给出错误。
const App: React.FC = () => {
const ref = useRef(null);
useEffect(() => {
ref.current.focus();
}, []);
return (
<>
<Child ref={ref} />
</>
);
};
const Child = forwardRef((props, ref: Ref<any>) => {
return <input type="text" name="child" ref={ref} />;
});
注意:React.forwardRef参数必须是function,而这个API通常用来解决HOC(高阶组件)中丢失ref的问题。
useImperativeHandle
在forwardRef例子中的代码实际上是不推荐的,因为无法控制要暴露给父组件的值,所以我们使用useImperativeHandle控制要将哪些东西暴露给父组件。
useImperativeHandle 应当与 forwardRef 一起使用
调用方式:
useImperativeHandle(ref, createHandle, [deps])
- 接收一个ref
- 接收一个函数,这个函数返回的对象即是要暴露出的ref
- 类似useEffect,接收一个依赖数组
const FancyInput=(props, ref) =>{
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
}
export default forwardRef(FancyInput);
在本例中,渲染 的父组件可以调用 inputRef.current.focus()