近期做需求的时候,有个父组件需要调用子组件的场景,经过一番思考,于是就有了下面的代码
使用 props 实现
Father.js 父组件
function Father() {
const ref = useRef();
const onSonSay = () => {
ref.current()
}
const callback = (cb) => {
ref.current = cb;
}
return (
<>
<button onClick={onSonSay}>调用子组件的 say 方法</button>
<Son callback={callback}></Son>
</>
)
}
son.js
function Son(props) {
useEffect(() => {
props.callback(say)
}, [])
const say = () => {
console.log('son say')
}
return <div>son</div>
}
上述代码也是可以实现父组件调用子组件的 say 方法的,逻辑稍微有一点绕,通俗一点讲就是父组件通过一个函数把子组件的函数捞起并保存在父组件里
- 父组件向子组件传递一个函数 props — callback
- 子组件在 didMount 阶段执行并把需要暴露给父组件的函数当做参数传递给 callback
- 父组件实现 callback, 用一个 ref 接收子组件传回来的函数
- 点击父组件 button 时,执行 ref.current(), 即执行子组件函数
使用 useImperativeHandle 实现
查阅资料以后,发现 React 提供了一个 hook — useImperativeHandle, 可以实现上述功能
先看一下官网定义
useImperativeHandle
is a React Hook that lets you customize the handle exposed as a ref.
翻译过来就是
useImperativeHandle 是一个 React Hook, 可以当做一个 ref 来让你自定义暴露的句柄
好吧, 翻译过来其实也不是很明白🐶,我们直接实现一个简单的例子,然后再讲解其用法
Father.js
function Father() {
const ref = useRef();
const onSonSay = () => {
ref.current.say();
}
return (
<>
<button onClick={onSonSay}>调用子组件的 say 方法</button>
<Son ref={ref}></Son>
</>
)
}
Son.js
const Son = forwardRef(function (props, ref) {
useImperativeHandle(ref, () => {
return { say }
}, [])
const say = () => {
console.log('son say')
}
return <div>son</div>
})
首先看父组件的更改
- 给子组件绑定了一个 ref
- 调用子组件的 say 方法直接使用 ref.current.say()
子组件的更改
- 使用了 forwardRef 包裹子组件, 因为要将 ref 传递到子组件内,所以需要使用 forwordRef
- 使用 useImperativeHandle hook 实现将子组件的 say 方法抛给父组件
如上,就实现了父组件调用子组件的方法, 而 useImperativeHandle 第二个参数的返回值即为暴露给父组件的方法或元素
再去看 useImperativeHandle 的定义,是不是就清楚了一些了呢
语法
通过上述 demo, 可以发现 useImperativeHandle 接受三个参数,
- 第一个参数是从 forwardRef 渲染函数收到的作为第二个参数的 ref
- 第二个参数是个回调函数,用于将需要暴露的元素或方法返回出去,绑定到 ref 上
- 第三个参数是一个依赖项,类似于 useEffect 的依赖, 如果依赖数组内部传递的数据发生改变,就会重新触发回调函数
总结
官网的例子写的很不错, useImperativeHandle 也可以多层嵌套,父组件可以通过子组件调用孙组件的方法,这个 api 比较冷门,但是很好用。
参考:https://react.dev/reference/react/useImperativeHandle