React Hook 获取子组件的方法和数据

5,093 阅读3分钟

「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

在 class 组件中,可以通过 ref 的方式获取子组件中的方法和State,而在 function 组件中,可以通过 Hook 来获取子组件的方法和数据。

在 function 组件中使用 ref 时,我们可以借助 useImperativeHandle 将子组件的 State 或 方法暴露给父组件。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用。

ref 和 forwardRef

我们先来回顾一下 ref 和 forwardRef 的使用:

  • 在父组件中通过 useRef() 创建一个 ref

  • 在父组件中引用子组件时给子组件添加 ref 属性

  • 子组件使用 forwardRef 包裹

  • 子组件拿到父组件创建的 ref,绑定到自己的某一个元素中

    import React, { useRef, forwardRef } from 'react'

    // forwardRef 可以将 ref 转发给子组件 const Child = forwardRef((props, ref) => { return })

    export default function Parent() { // childRef 用于获取函数式组件DOM元素 const childRef = useRef() const getFocus = () => { childRef.current.focus() }

    return (

    聚焦 {/* 给子组件添加 ref 属性 */}
    ) }

forwardRef 可以将 refs 转发到 DOM 组件,从而可以获取到具体的DOM元素。但是它的这种做法会使得子组件的DOM直接暴露给父组件:

  • 直接暴露给父组件所带来的问题是某些情况的不可控

  • 父组件拿到子组件的DOM后可以进行任意的操作

  • 我们只希望父组件可以操作 input 的focus,但并不希望它随意操作其它的方法

为了避免这样的情况,我们可以在子组件中使用 useImperativeHandle,通过它只暴露特定的方法或数据。

useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])

在子组件中,通过 useImperativeHandle 这个 hook,将父组件传入的 ref 和 useImperativeHandle 的第二个参数返回的对象绑定到了一起,所以当我们在父组件中调用 childRef.current 时,实际上拿到的是 useImperativeHandle 的第二个参数返回的对象,在这个对象中,是我们自定义的暴露给父组件的方法或者数据。

import React, { useRef, forwardRef, useImperativeHandle } from 'react'

const Child = forwardRef((props, ref) => {
  const inputRef = useRef();
  
  // 作用: 减少父组件获取子组件的DOM元素属性,只暴露给父组件需要用到的DOM方法
  // 参数1: 父组件传递的ref属性
  // 参数2: 返回一个对象,父组件通过ref.current调用对象中方法
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus()
    },
  }))
  return <input type="text" ref={inputRef} />
})

export default function Parent() {
  // useImperativeHandle 主要作用:用于减少父组件中通过forwardRef + useRef 获取子组件DOM元素暴露的属性过多
  // 为什么使用: 因为使用forwardRef + useRef 获取子函数式组件DOM时,获取到的dom属性暴露的太多了
  // 解决: 使用uesImperativeHandle解决,在子函数式组件中定义父组件需要进行DOM操作,减少获取DOM暴露的属性过多
  const childRef = useRef()

  return (
    <div>
      <button onClick={() => childRef.current.focus()}>聚焦</button>
      <Child ref={childRef} />
    </div>
  )
}

useImperativeHandle使用简单总结:

  • 作用: 减少暴露给父组件获取的DOM元素属性, 只暴露给父组件需要用到的DOM方法

  • 参数 ref: 父组件传递的ref属性

  • 参数 createHandle: 返回一个对象, 以供给父组件中通过ref.current调用该对象中的方法