antd2.x中Form组件踩坑

183 阅读2分钟

问题背景

在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,得到的是表单的方法

image-20230428160226597

代码实现:

// 父组件为类组件
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

image-20230502191259717

代码实现:

// 父组件中
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官方解答:

image-20230428162443591

方法2:通过useImperativeHandle拿到正确的实例对象

代码实现:

// 父组件中
 SonRef = createRef()
  render() {
    return (
      <div>
        <Son 
          SonRef={this.SonRef}
        />
      </div>
    )
  }
// 子组件中使用父组件传下来的实例
 useImperativeHandle(props.SonRef, () => (
    {
      succ,
    }
  ))

image-20230502192631328

useImperativeHandle接收两个参数:

参数1:ref - 一个可变的ref对象,通常是通过父组件传递给子组件的。通过该ref对象,可以在父组件中直接访问子组件的属性或方法。

参数2:createHandle - 一个函数,用于创建将被暴露给父组件的方法或属性。

  • 为什么在父组件中直接使用ref绑定子组件不能获得正确的实例对象?
  1. ref属性的作用是来将某个DOM元素或者组件实例保存到ref对象中,从而可以在父组件中访问到该子组件中的DOM元素或者组件实例。
  2. 当子组件被HOC包装之后,其实际的类型会发生变化,因此在父组件中使用ref来引用子组件时,可能会引用到错误的组件实例。
  3. 为此,需要使用forwardRef方法来将ref传递给子组件,以确保父组件中引用到的是正确的组件实例。

总结

以上是工作中遇到的小坑,虽然不难解决,但是可以加深对React类组件以及函数组件的理解。

谢谢各位爷赏脸观看。