React+Zustand通知系统中useEffect如何避免误清除运行中timeout
问题根因
你当前的故障核心原因是:外层Notifications组件的useEffect依赖了整个queue数组,只要队列新增/删除元素导致queue引用变化,就会触发上一次渲染的清理函数,你之前的清理逻辑固定清除队列第一条/最后一条的timeout,自然会误清除还未到期的定时任务。
而且你已经在notificate方法中为每个通知单独绑定了到期自动删除的定时逻辑,外层的这个useEffect属于完全多余的冗余逻辑,反而引发了故障。
解决方案
- 首先删除外层组件中依赖
queue的冗余useEffect,新增通知时不会再触发多余的清理动作,各个通知自带的定时逻辑可以正常运行互不干扰。 - 可选优化(避免内存泄露):
- 在外层组件仅加卸载时的全量清理逻辑,依赖项留空,仅在整个通知组件卸载时执行,不会因队列变化触发
- 在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
相关产品推荐
相关产品推荐

