「这是我参与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调用该对象中的方法