聊天应用中handleReadChat滚动触发次数过多,如何重构代码?
解决方案
核心问题分析
scroll事件会在滚动过程中高频触发,你的代码每次符合条件就创建新的setTimeout,且没有做重复触发拦截,导致大量重复任务堆积执行。同时两个独立的scroll监听也可以合并优化。
重构后的代码
import { useCallback, useEffect, useRef, useState } from 'react'; // ... 你的其他业务代码 const ChatRoom = () => { const refEntries = useRef(null); const [showScrollButton, setShowScrollButton] = useState(false); // 存储防抖定时器ID,避免重复触发 const readDebounceTimer = useRef(null); // 标记是否已触发过已读操作,同一状态下避免重复执行 const hasTriggeredRead = useRef(false); const handleScroll = useCallback(() => { const entry = refEntries.current; if (!entry) return; const isNearBottom = entry.scrollTop > entry.scrollHeight - 800; // 处理滚动按钮显示逻辑 setShowScrollButton(!isNearBottom); // 处理标记已读逻辑 if ( isNearBottom && activeRoom && conversations?.content?.length > 0 && activeRoom.unread && !hasTriggeredRead.current ) { // 先清除之前的定时器,避免重复排队 if (readDebounceTimer.current) { clearTimeout(readDebounceTimer.current); } // 用户停止滚动后再执行,延迟时间可按需调整 readDebounceTimer.current = setTimeout(() => { dispatch(readConversationByRoom(activeRoom.id)); console.log("executed", activeRoom.unread); // 标记已触发,直到状态变更再重置 hasTriggeredRead.current = true; }, 500); } else if (!isNearBottom || !activeRoom?.unread) { // 离开底部或房间无未读时,重置触发标记 hasTriggeredRead.current = false; // 清除未执行的定时器 if (readDebounceTimer.current) { clearTimeout(readDebounceTimer.current); readDebounceTimer.current = null; } } }, [activeRoom, conversations?.content, dispatch]); useEffect(() => { const entry = refEntries.current; if (!entry) return; entry.addEventListener("scroll", handleScroll); return () => { entry.removeEventListener("scroll", handleScroll); // 组件卸载时清理定时器 if (readDebounceTimer.current) { clearTimeout(readDebounceTimer.current); } }; }, [handleScroll]); // 切换房间或未读状态变化时,重置触发标记 useEffect(() => { hasTriggeredRead.current = false; }, [activeRoom?.id, activeRoom?.unread]); // ... 你的其他组件渲染代码 };
关键优化点
- 合并滚动监听:把滚动按钮显示和已读标记逻辑合并到同一个函数,减少事件监听的性能开销。
- 防抖处理:用定时器控制执行时机,确保只有用户停止滚动后才触发标记操作,避免滚动过程中高频触发。
- 重复触发拦截:通过
hasTriggeredRead标记状态,同一房间未读状态未变更时,停留在底部不会重复执行标记操作。 - 状态重置机制:切换房间、未读状态变化或离开底部时,自动重置触发标记,保证后续符合条件时能正常触发。
内容的提问来源于stack exchange,提问作者Owenn
相关产品推荐
相关产品推荐

