本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
如果你遇到一个十分复杂的页面,不同的组件不知道如何关联,那么你可能就要知道一个知识点:共享事件通知
在一个公共页面内,里面有很多模块,这时我们将这个页面划分多个组件,然后共同形成这个整体的页面,每个组件都是相互独立,但又有关联,而如何关联你需要使用 useImperativeHandle 或 useEventEmitter 来完成
使用场景以下面为例:
假设在一个页面内,我们有 A 和 B 两个组件,还有一个底层的按钮,在A组件中有个数字累加器的功能,触发按钮A会使累加器加1,点击按钮B和按钮C也会触发组件A的点击效果,使累加器加1。
注: 组件A 和 组件B 是兄弟关系,组件A 和 按钮C是父子关系
当我们想达到这个效果,就需要 useImperativeHandle 或 useEventEmitter 的帮助,接下来,将分别讲下这两个钩子的使用和区别
在线地址:Domesy/ahooks
useImperativeHandle
useImperativeHandle 是 React 官方提供的一个钩子,其作用是 可以让你在使用 ref 时自定义暴露给父组件的实例值。,
我们直接以这篇文章的例子来说明:还不懂 Hook?那你是真的Low了
先上代码:
import React, { useState, useImperativeHandle, useRef } from 'react';
import { Button } from 'antd';
const Children: React.FC<any> = ({cRef}) => {
const [count, setCount] = useState<number>(0)
const add = () => {
setCount((c) => c + 1)
}
useImperativeHandle(cRef, () => ({
add
}))
return <div style={{marginBottom: 20}}>
<p>点击次数:{count}</p>
<Button type='primary' onClick={() => add()}>加1</Button>
</div>
}
const Mock: React.FC<any> = () => {
const ref = useRef<any>(null)
return (
<div>
<Children cRef={ref} />
<Button type='primary' onClick={() => {
ref.current.add()
}}>父节点加1</Button>
</div>
);
};
export default Mock;
代码分析:
- 我们在父组件
Mock传递一个ref给子组件Children - 子组件通过传递的
ref作为第一个参数传递给useImperativeHandle,useImperativeHandle的第二各参数在把children的点击方法add暴露出去 - 最后父组件
Mock在将暴露的方法ref.current.add()使用即可,就能完成共享事件通知
useEventEmitter
useEventEmitter: 是 ahooks 提供的一个方法,适合的是在距离较远的组件之间进行事件通知,或是在多个组件之间共享事件通知(而非参数的共享)
先简单介绍下useEventEmitter的使用方法,在通过案例去讲解
如何使用: const click = useEventEmitter();
绑定事件(订阅事件): click.useSubscription(callback: (val: T) => void)=> void
调取事件(发送通知): click.emit(val: T) => void
代码示例
import React, { useState, useRef } from 'react';
import { Button } from 'antd';
import { useEventEmitter } from 'ahooks';
import { EventEmitter } from 'ahooks/lib/useEventEmitter';
const Children: React.FC<{click: EventEmitter<number>}> = ({ click }) => {
const [ count, setCount ] = useState<number>(0);
const ref = useRef<any>(null)
click.useSubscription((val) => {
val === 1 ? message.info('父组件点击的') : message.info('兄弟节点点击的')
ref.current.click();
})
const onClick = () => {
setCount(v => v+1)
}
return <>
<div>点击次数:{count}</div>
<Button ref={ref} type='primary' style={{margin: '8px 0'}} onClick={onClick} >子组件点击</Button>
</>
}
const Children1: React.FC<{click: EventEmitter<number>}> = ({click}) => {
return <Button style={{marginTop: 8}} type="primary" onClick={() => click.emit(2)}>兄弟节点点击</Button>
}
const Mock: React.FC<any> = () => {
const click = useEventEmitter<number>();
return (
<>
<Children click={click} />
<div>
<Button type='primary' onClick={() => {
click.emit(1)
}} >父组件组件点击</Button>
</div>
<Children1 click={click} ></Children1>
</>
);
};
export default Mock;
分析代码:
- 首先在父组件
Mock中,传递一个点击方法click(useEventEmitter) 给子组件Children - 其次在子组件
Children中通过传递的click进行绑定,使用useSubscription进行绑定,用Ref获取到按钮的点击方法,然后执行 - 然后通过
emit进行消费,就能完成共享事件通知
useImperativeHandle 与 useEventEmitter 对比
从上述的案例中,我们发现 useImperativeHandle 是通关传递 ref 来作为参数,useEventEmitter通过订阅和派发来完成,两者其实差不多,但 useEventEmitter更加适用于距离较远的组件通知,或多个租件之间的共享通知
我记得useImperativeHandle多个组件之间不能共享, useEventEmitter相当于useImperativeHandle的增强版,以上只是本菜鸟的个人观点,如果不对,欢迎评论区讨论(● ̄(エ) ̄●)