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

聊天应用中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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 13:33:44