You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Zustand通知系统中useEffect如何避免误清除运行中timeout

问题根因

你当前的故障核心原因是:外层Notifications组件的useEffect依赖了整个queue数组,只要队列新增/删除元素导致queue引用变化,就会触发上一次渲染的清理函数,你之前的清理逻辑固定清除队列第一条/最后一条的timeout,自然会误清除还未到期的定时任务。
而且你已经在notificate方法中为每个通知单独绑定了到期自动删除的定时逻辑,外层的这个useEffect属于完全多余的冗余逻辑,反而引发了故障。

解决方案

  1. 首先删除外层组件中依赖queue的冗余useEffect,新增通知时不会再触发多余的清理动作,各个通知自带的定时逻辑可以正常运行互不干扰。
  2. 可选优化(避免内存泄露):
    • 在外层组件仅加卸载时的全量清理逻辑,依赖项留空,仅在整个通知组件卸载时执行,不会因队列变化触发
    • 在store的rm方法中补充单条通知的timeout清理,避免用户手动提前关闭通知时,对应的timeout还在内存中待执行

修改后的Notifications组件代码

export const Notifications = () => {  
  const { queue } = useStore()

  // 仅组件卸载时清理所有未执行的timeout,防止内存泄露
  useEffect(() => {
    return () => {
      queue.forEach(notification => {
        notification.timeout && clearTimeout(notification.timeout)
      })
    }
  }, [])

  return (
    <div className="absolute bottom-0 mb-8 space-y-3">
      {queue.map(({ id, value }) => (
        <NotificationComponent key={id} requestDiscard={() => id}>
          {value}
        </NotificationComponent>
      ))}
    </div>
  ) 
}

优化后的Zustand store代码

interface State {
  queue: Notification[]
  add: (notification: Notification) => void
  rm: (id: string) => void
}

const useNotificationStore = c<State>(set => ({
  add: (notification: Notification) =>
    set(({ queue }) => ({ queue: [...queue, notification] })),
  rm: (id: string) =>
    set(({ queue }) => {
      // 清理待删除通知对应的timeout
      const targetNotification = queue.find(item => item.id === id)
      targetNotification?.timeout && clearTimeout(targetNotification.timeout)
      return {
        queue: queue.filter(n => id !== n.id),
      }
    }),
  queue: [],
}))

修改后新增通知时不会影响已有通知的定时任务,所有timeout只会在对应通知到期删除、手动删除、或者整个通知组件卸载时被清理,完全符合预期。

内容的提问来源于stack exchange,提问作者Bas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 11:24:10