React 调用函数子组件上的方法

前言

最近学习使用React Hooks时,父组件使用ref到函数组件上时出现:

Function components cannot be given refs.

函数组件不能被给予refs的错误提示,那么我该如何在父组件上调用子组件的方法呢。

参考:react useImperativeHandle

解决

举一个简单的案例,父组件上有一个Button按钮,用户点击按钮后显示子组件的Modal弹窗让用户填写数据。那么我们就需要通过父组件调用子组件改变Modal显示状态的方法让弹窗显示出来。

父组件

父组件使用useRef钩子创建一个可变的ref对象,然后把这个ref传递给子组件。父组件上使用ref对象的current属性调用子组件上的方法,

import React, { useRef } from 'react'
import { Button } from 'antd'

const Parent = () => {
  // useRef 返回一个ref对象
  const modal = useRef(null)

  const showModal = () => {
    // 调用子组件上的 changeVisible 方法
    modal.current.changeVisible()
  }

  return (
    <>
      <Button onClick={showModal}>添加数据</Button>
      <Child ref={modal} />
    </>
  )
}

export default Parent

子组件

forwardRef会把组件接受到的ref属性作为组件的第二个参数,然后通过useImperativeHandle自定义需要暴露给父组件的方法。

useImperativeHandle钩子第一个参数为组件接受的ref,第二个参数为返回一个对象的函数。

import React, {
  useState,
  forwardRef,
  useImperativeHandle,
} from 'react'
import { Modal, Form } from 'antd'

const Child = (props, ref) => {
  const [visible, setVisible] = useState(false)
  // useImperativeHandle 可以自定义暴露给父组件的方法
  // 与 forwardRef 一起使用
  useImperativeHandle(ref, () => ({
    changeVisible() {
      setVisible(!visible)
    }
  }))

  return (
    <Modal visible={visible}>
      <Form></Form>
    </Modal>
  )
}

export default forwardRef(Child)

补充

使用useImperativeHandle钩子第二个参数函数返回对象中定义的方法在该函数组件当然是无法调用的。如果该子组件也需要使用这个方法的话,我们就放在外面定义,然后在useImperativeHandle中使用就可以啦。

useImperativeHandle(ref, () => ({ changeVisible }))
const changeVisible = () => setVisible(!visible)

这样使用ES6的语法简写后看上去也比较舒服了。

PropTypes

通常我们都会使用到prop-typespops进行类型检查,但是向上面的方法那样使用forwardRef钩子创建组件后会出现一个警告:

Warning: forwardRef render functions do not support propTypes or defaultProps. 

提示不支持propTypesdefaultProps了,所有我们应该这样创建函数组件:

import React, {
  useState,
  forwardRef,
  useImperativeHandle,
} from 'react'
import PropTypes from 'prop-types'
import { Modal, Form } from 'antd'

const Child = forwardRef(function Component(props, ref) {
  const [visible, setVisible] = useState(false)
  useImperativeHandle(ref, () => ({
    changeVisible() {
      setVisible(!visible)
    }
  }))

  return (
    <Modal visible={visible}>
      <Form></Form>
    </Modal>
  )
})

// 这样就可以使用 propTypes 啦
Child.propTypes = {}

export default Child

细心的小伙伴会发现forwardRef钩子传入的组件并不是匿名组件,这是因为匿名组件的话使用eslint规范检测的时候会出现一个错误:

Component definition is missing display name  react/display-name

版权声明:

Anand's Blog文章皆为站长Anand Zhang原创内容,转载请注明出处。

包括商业转载在内,注明下方要求的文章出处信息即可,无需联系站长授权。

请尊重他人劳动成果,用爱发电十分不易,谢谢!

请注明出处:

本文出自:Anand's Blog

本文永久链接:https://anandzhang.com/posts/frontend/18