forwardRef
React.forwardRef字面意思理解为转发Ref,它会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。其主要作用是:
1.转发refs到DOM组件(ref不像props作为参数可以传递,所以要想传递ref得用forwardRef)
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
因此,当 React 附加了 ref 属性之后,ref.current 将直接指向 DOM 元素实例。
如果一个组件被多次使用,正常情况下想要调用其组件内的方法需要传入props来调用,每次传入的话就比较多余。所以就引入了React.forwardRef。(在tsx中的运用)
interface SelectFileModalRef {
handleShowModal: () => void;
handleCancel: () => void;
}
const SelectFileModal = forwardRef<SelectFileModalRef, Props>(
(props: Props, ref: Ref<SelectFileModalRef>) => {
useImperativeHandle(ref, () => ({
handleShowModal,
handleCancel,
}));
}
);
子组件暴露出来的handleShowModal和handleCancel在父组件就可以使用selectFileModalRef.current?.handleShowModal() 来调用进行使用。
2.在高阶组件中转发 refs
首先要搞清楚什么是高阶组件:高阶组件(HOC)它是一种基于 React 的组合特性而形成的设计模式。
具体而言,高阶组件是参数为组件,返回值为新组件的函数
// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
// ...并返回另一个组件...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
// ...负责订阅相关的操作...
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
// ... 并使用新数据渲染被包装的组件!
// 请注意,我们可能还会传递其他属性
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。
此时需要用到forwardRef
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
useImperativeHandle
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。通常与forwardRef一起使用,暴露之后父组件就可以通过
selectFileModalRef.current?.handleCancel();
来调用子组件的暴露方法。
useImperativeHandle(ref, () => ({
handleShowModal,
handleCancel,
}));