import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';interface AutoScrollListProps<T> {data: T[];itemContent: (index: number, item: T) => React.ReactNode;initialTopMostItemIndex?: number;className?: string;
}const AutoScrollList = <T,>({data,itemContent,initialTopMostItemIndex,className,
}: AutoScrollListProps<T>): React.ReactElement => {const virtuosoRef = useRef<VirtuosoHandle>(null);const [isAtBottom, setIsAtBottom] = useState(true);const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);// 检查是否接近底部(5px范围内)const checkIfNearBottom = useCallback((scrollTop: number, scrollHeight: number, clientHeight: number) => {const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);return distanceFromBottom <= 5;}, []);// 滚动处理函数const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {if (scrollTimeoutRef.current) {clearTimeout(scrollTimeoutRef.current);}const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;const nearBottom = checkIfNearBottom(scrollTop, scrollHeight, clientHeight);// 使用防抖避免频繁触发scrollTimeoutRef.current = setTimeout(() => {setIsAtBottom(nearBottom);}, 100);}, [checkIfNearBottom]);// 当数据变化且之前接近底部时,自动滚动到底部useEffect(() => {if (isAtBottom && virtuosoRef.current && data.length > 0) {virtuosoRef.current.scrollToIndex({index: data.length - 1,behavior: 'smooth',});}}, [data.length, isAtBottom]);// 清理定时器useEffect(() => {return () => {if (scrollTimeoutRef.current) {clearTimeout(scrollTimeoutRef.current);}};}, []);return (<Virtuosoref={virtuosoRef}data={data}itemContent={itemContent}initialTopMostItemIndex={initialTopMostItemIndex !== undefined ? initialTopMostItemIndex : 0}followOutput={isAtBottom ? 'smooth' : false}alignToBottom={false}atBottomThreshold={5}atBottomStateChange={setIsAtBottom}onScroll={handleScroll}className={`w-full h-full ${className || ''}`}/>);
};export default AutoScrollList;
‘use client’
import React, { useState, useEffect } from ‘react’;
import AutoScrollList from ‘./HighPerformanceScroller’;
interface Message {
id: number;
text: string;
}
const ExampleComponent: React.FC = () => {
// 初始化一些消息,确保页面有内容
const [messages, setMessages] = useState<Message[]>([
{ id: 0, text: ‘初始消息 1’ },
{ id: 1, text: ‘初始消息 2’ },
{ id: 2, text: ‘初始消息 3’ },
]);
const MAX_MESSAGES = 100; // 限制最大消息数量
// 模拟添加新消息
useEffect(() => {
console.log(‘组件挂载,初始消息数量:’, messages.length);
const interval = setInterval(() => {setMessages(prev => {const newMessage = { id: prev.length, text: `消息 ${prev.length + 1}` };const newMessages = [...prev, newMessage];console.log('添加新消息,当前消息数量:', newMessages.length);// 如果消息超过最大数量,删除最旧的消息if (newMessages.length > MAX_MESSAGES) {return newMessages.slice(newMessages.length - MAX_MESSAGES);}return newMessages;});
}, 3000);return () => clearInterval(interval);
}, []);
console.log(‘渲染组件,消息数量:’, messages.length);
return (
自动滚动消息列表 ({messages.length}条消息)
{messages.length === 0 ? (
) : (
<AutoScrollList
data={messages}
itemContent={(index: number, message: Message) => (
<div className={
p-3 border-b border-gray-200 ${index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}
}> {index + 1}.
{message.text}
)}
/>
)}
);
};
export default ExampleComponent;