问题背景
在React项目中,由于工作需要,选用了React16.12 + Antdesign 2.x
rc-form的版本为1.4x;
在使用被Form组件包裹的子组件时,无法通过ref拿到子组件实例上的方法
只能拿到Form本身的方法
原因与分析
Antd2.x中子组件如果想要使用Form组件的方法,在导出时需要通过Form.create()(componentName)进行处理。
通过ref,拿到的是Form的实例,上面只有Form的方法,而不是子组件本身的。
当使用ref访问高阶组件包装的子组件时,可以得到高阶组件的实例,而不是子组件的实例。
这是因为ref引用的是组件的实例,而不是组件本身。在高阶组件中返回的新组件实例,实际上是高阶组件本身,而不是被包装的子组件。
问题复现
在父组件中直接使用ref绑定子组件
在父组件中打印this.SonRef,得到的是表单的方法
代码实现:
// 父组件为类组件
export default class FatherComponent extends PureComponent {
SonRef = createRef()
render() {
return (
<div>
<Son
ref={this.SonRef}
/>
</div>
)
}
}
// 子组件为函数组件
const Son = memo(forwardRef((props,ref) => {
const {getFieldDecorator} = props.form;
const succ = () => console.log('succ~')
return (
<div>
<Form>
<Form.Item>
{getFieldDecorator('input')(
<Input />
)}
</Form.Item>
</Form>
</div>
)
}))
export default Form.create()(Son)
解决方法
方法1:使用wrappedComponentRef
代码实现:
// 父组件中
SonRef = createRef()
<Son wrappedComponentRef={ref => this.SonRef = ref}/>
// 子组件是函数组件
const Son = memo(forwardRef((props,ref) => {
const {getFieldDecorator} = props.form;
const succ = () => console.log('succ~')
useImperativeHandle(ref, () => (
{
succ,
}
))
return (
<div>
<Form>
<Form.Item>
{getFieldDecorator('input')(
<Input />
)}
</Form.Item>
</Form>
</div>
)
}))
export default Form.create()(Son)
什么是wrappedComponentRef?
antd官方解答:
方法2:通过useImperativeHandle拿到正确的实例对象
代码实现:
// 父组件中
SonRef = createRef()
render() {
return (
<div>
<Son
SonRef={this.SonRef}
/>
</div>
)
}
// 子组件中使用父组件传下来的实例
useImperativeHandle(props.SonRef, () => (
{
succ,
}
))
useImperativeHandle接收两个参数:
参数1:ref - 一个可变的ref对象,通常是通过父组件传递给子组件的。通过该ref对象,可以在父组件中直接访问子组件的属性或方法。
参数2:createHandle - 一个函数,用于创建将被暴露给父组件的方法或属性。
- 为什么在父组件中直接使用ref绑定子组件不能获得正确的实例对象?
- ref属性的作用是来将某个DOM元素或者组件实例保存到ref对象中,从而可以在父组件中访问到该子组件中的DOM元素或者组件实例。
- 当子组件被HOC包装之后,其实际的类型会发生变化,因此在父组件中使用ref来引用子组件时,可能会引用到错误的组件实例。
- 为此,需要使用forwardRef方法来将ref传递给子组件,以确保父组件中引用到的是正确的组件实例。
总结
以上是工作中遇到的小坑,虽然不难解决,但是可以加深对React类组件以及函数组件的理解。
谢谢各位爷赏脸观看。