关于forwardRef的使用做一下总结。
转发ref的目的是为了不会出现this.ref.ref.ref
之类的情况,可以对外界暴露内部的ref,因此称为转发(forward)。
应用场景:
- 获取深层次子孙组件的 DOM 元素
- 在函数式组件上使用ref赋值(函数组件本身上ref的是无效的,因为它自己没有实例,但
forwardRef
返回的组件可以) - 传递
refs
到高阶组件
需要了解的:
ref
并非react元素的属性,它经过了特殊处理,因此不能从props
取得,类似的属性还有key
。React.forwardRef
只能用于函数组件
;React.forwardRef
返回高阶组件(HOC)
;React.forwardRef
的参数是一个函数组件
或者说render函数
;render函数组件
第二个参数是ref
,平时这个参数在函数组件中没用,只有作为forwardRef参数
时才有效。
注意:forwardRef并不能解决直接选取到第三方组件库中的dom的作用,除非对方提供了forwardRef的支持。
目录
最基本的使用
import React, { useRef, useImperativeHandle } from "react";
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
myfocus: () => {
inputRef.current.focus();
},
myblur: () => {
inputRef.current.blur();
}
}));
return <input ref={inputRef} />;
}
const FancyInputWrapped = React.forwardRef(FancyInput);
const App = () => {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.myfocus();
};
const handleBlur = () => {
inputRef.current.myblur();
};
return (
<div>
<FancyInputWrapped ref={inputRef} />
<button style={{ marginLeft: 8 }} onClick={handleFocus}>
FOCUS
</button>
<button style={{ marginLeft: 8 }} onClick={handleBlur}>
BLUR
</button>
</div>
);
};
export default () => <App />;
查看:https://codesandbox.io/s/basic-forward-ref-2hjwky
高阶组件转发
注:实际上,我并没有从这个例子中看到特别有用的部分,其实我们没有fowardRef一样可以实现这个过程,如果你不想用forwardRef解决完全是可以的。
import React from "react";
const FocusInput = React.forwardRef((props, ref) => (
<input type="text" ref={ref} />
));
function enhance(WrappedComponent) {
class Enhance extends React.Component {
render() {
const { forwardedRef, ...restProps } = this.props;
// 将定义的 prop 属性 forwardRef 定义为 ref
return <WrappedComponent ref={forwardedRef} {...restProps} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 ref
// 我们可以将其作为常规 prop 属性传递给 Enhance,例如 forwardedRef
// 然后它就可以被挂载到被 Enhance 包裹的子组件上
return React.forwardRef((props, ref) => (
<Enhance {...props} forwardedRef={ref} />
));
}
// EnhancedChildComponet 会渲染一个高阶组件 enhance(FocusInput)
const EnhancedChildComponet = enhance(FocusInput);
// 我们导入的 EnahcnedComponent 组件是高阶组件(HOC)Enhance
// 通过 React.forward 将 ref 将指向了 Enhance 内部的 FocusInput 组件
// 这意味着我们可以直接调用 ref.current.focus() 方法
class App extends React.Component {
private ref = React.createRef<HTMLInputElement>();
constructor(props) {
super(props);
}
handleFocus = () => {
const { current } = this.ref;
current.focus();
};
handleBlur = () => {
const { current } = this.ref;
current.blur();
};
render() {
return (
<>
<EnhancedChildComponet ref={this.ref} />
<button style={{ marginLeft: 8 }} onClick={this.handleFocus}>
FOCUS
</button>
<button style={{ marginLeft: 8 }} onClick={this.handleBlur}>
BLUR
</button>
</>
);
}
}
export default () => <App />;
查看:https://codesandbox.io/s/hoc-foward-ref-8z727h?file=/App.tsx
useImperativeHandle
此Hook必须配合forwardRef使用,其作用看起来是封装自己ref对象上的方法,其实也不是特别必要使用的技术。
import React, { useRef, useImperativeHandle } from 'react';
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
myfocus: () => {
inputRef.current.focus();
},
myblur: () => {
inputRef.current.blur();
}
}));
return <input ref={inputRef} />;
}
const FancyInputWrapped = React.forwardRef(FancyInput);
const App = () => {
const inputRef = useRef(null)
const handleFocus = () => {
inputRef.current.myfocus();
}
const handleBlur = () => {
inputRef.current.myblur();
}
return (
<div>
<FancyInputWrapped ref={inputRef} />
<button style={{ marginLeft: 8 }} onClick={handleFocus}>FOCUS</button>
<button style={{ marginLeft: 8 }} onClick={handleBlur}>BLUR</button>
</div>
)
};
export default () => <App />
查看:https://codesandbox.io/s/ji-ben-yong-fa-forked-2hjwky?file=/App.tsx