react 实现监听逻辑

需求:

  • 在一个页面下有多个子tab
  • 在某些tab 下,或者父节点的数据更新的时候,其他子tab 或者父节点也要同步更新

进程:

  • 正常情况下会把所有用到的数据都移动到父节点,修改行为也都放在父节点
  • 但如果这样的话父节点的数据会非常的多,而且有可能只是某两个子节点的数据需要更新,这就很麻烦
  • 换一个思路,所有的数据都放在各自子节点但是,在某个变化发生的时候,触发一下更新逻辑
  • 那又回到之前的那个问题了,需要每一个子节点都传入对应的触发函数
  • 再换一个逻辑,增加一个全局的hook 并绑定上对应的事件,在你想要触发更新的时候调用一下这个hook 行为,如果有绑定的事件的话,就会触发对应事件
typescript 复制代码
//eventhook
type eventName = string | Symbol

const eventMap = new Map<eventName, Array<Function>>()

const addListenerByName = (name: eventName, func: Function) => {
  if (typeof func !== 'function') {
    return ;
  }
  const listeners = eventMap.get(name) || []
  if (!listeners.includes(func)) {
    eventMap.set(name, [...listeners, func])
  }
}

const removeListenersByName = (name: eventName, func: Function) => {
  const listeners = [...(eventMap.get(name) || [])]
  eventMap.set(name, listeners.filter(f => f !== func))
}

const triggerEventByName = (name: eventName) => {
  const listeners = eventMap.get(name) || []
  listeners.forEach(f => f())
}

type useEventReturn = {
  addListener: (func: Function) => () => void, // 调用返回的Function会remove监听
  removeListeners: (func: Function) => void,
  triggerEvent: () => void,
}
/**
 * 记得清除副作用
 * @param evenetName
 * @returns
 */
export const useEvent = (name: eventName = '@@init'): useEventReturn => {
  return {
    addListener: func => {
      addListenerByName(name, func)
      return () => removeListenersByName(name, func);
    },
    removeListeners: func => removeListenersByName(name, func),
    triggerEvent: () => triggerEventByName(name),
  };
}
  • 使用的时候 export 一个固定的名字 export const UPDATEUSERDATAEVENTNAME = Symbol('event_name')
  • use 这个hook const { addListener } = useEvent(UPDATEUSERDATAEVENTNAME)
  • 在需要被监听的地方在初始化的时候加入对应的监听逻辑
typescript 复制代码
useEffect(() => addListener(() => actionRef.current?.reload()), [])
  • 在需要触发这个监听逻辑的地方加入const { triggerEvent } = useEvent(UPDATEUSERDATAEVENTNAME)
  • 并触发这个监听
typescript 复制代码
useEffect(() => {
    triggerEvent()
  }, [userId] )
  • 这个做法的好处是就不需要往子组建传一堆东西了,而且也做到了更好的解耦

拓展

  • 在使用这个方法的时候需要一个全局唯一的名字所以用到了 Symbol
  • Symbol是一个绝对唯一的常亮,极端的例子 Symbol("test") != Symbol("test")
相关推荐
paopaokaka_luck2 小时前
宠物门店经营管理系统(销售与库存联动、进货自动记账、 退换与退款关联、员工个人业绩、ECharts 经营分析、滞销识别与客户分层、宠物档案年龄计算)
前端·javascript·spring boot·mysql·echarts·mybatis·1024程序员节
空中湖3 小时前
用 React 写数据大屏:为什么我不建议用图表库
前端·react.js·前端框架
鶴哥只手遮天6 小时前
jQuery 4.0 源码架构与工程实践-第 10 章 尺寸、几何与显隐
前端·javascript·jquery
米米商聊9 小时前
图标能点,按钮就可用了吗?键盘、名称与收藏状态
前端·javascript
技术小甜甜10 小时前
【Cursor】Skill 被标黄色警告,怎么改成合格的 SKILL.md
开发语言·javascript·ai编程·cursor·1024程序员节·ai agent·skills
qetfw11 小时前
Bash 函数与流程控制:read、if、for、case 验证
linux·前端·javascript·bash
鬼手点金12 小时前
从 API 消息到模型 Token:Chat Template
java·前端·javascript·数据库·人工智能·深度学习·1024程序员节
Mr.Pascal14 小时前
Javascript之什么是作用域?
javascript·1024程序员节
microrain14 小时前
设备报文怎么变,SagooIoT 只约定三个函数名
javascript·物联网·golang·sagooiot
Bug收容所14 小时前
Vue+JS Day3
前端·javascript·vue.js·前端框架·1024程序员节