mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
fix: Fix infinite scroll for conversations list
The useInfiniteScroll hook had a bug where the scroll event listener
might not be attached if the ref was assigned after the initial render.
This happened because the useEffect depended on handleScroll, which
didn't change when the ref was assigned via a callback ref.
Changes:
- Modified useInfiniteScroll to use a callback ref and state to track
when the container is mounted
- The hook now returns { ref, containerRef } instead of just the ref
- Updated ConversationPanel and RecentConversations to use the new API
This ensures that the scroll event listener is properly attached when
the container element is mounted, enabling auto-pagination to work
correctly in the conversations list.
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
afce58a27d
commit
d85952fe8e
@ -66,7 +66,7 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
|
||||
const { mutate: updateConversation } = useUpdateConversation();
|
||||
|
||||
// Set up infinite scroll
|
||||
const scrollContainerRef = useInfiniteScroll({
|
||||
const { ref: setScrollContainerRef } = useInfiniteScroll({
|
||||
hasNextPage: !!hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
@ -128,10 +128,9 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
|
||||
return (
|
||||
<div
|
||||
ref={(node) => {
|
||||
// TODO: Combine both refs somehow
|
||||
// Combine both refs
|
||||
if (ref.current !== node) ref.current = node;
|
||||
if (scrollContainerRef.current !== node)
|
||||
scrollContainerRef.current = node;
|
||||
setScrollContainerRef(node);
|
||||
}}
|
||||
data-testid="conversation-panel"
|
||||
className="w-full md:w-[400px] h-full border border-[#525252] bg-[#25272D] rounded-lg overflow-y-auto absolute custom-scrollbar-always"
|
||||
|
||||
@ -21,7 +21,7 @@ export function RecentConversations() {
|
||||
} = usePaginatedConversations(10);
|
||||
|
||||
// Set up infinite scroll
|
||||
const scrollContainerRef = useInfiniteScroll({
|
||||
const { ref: setScrollContainerRef } = useInfiniteScroll({
|
||||
hasNextPage: !!hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
@ -88,8 +88,11 @@ export function RecentConversations() {
|
||||
displayedConversations &&
|
||||
displayedConversations.length > 0 && (
|
||||
<div className="flex flex-col">
|
||||
<div className="transition-all duration-300 ease-in-out overflow-y-auto custom-scrollbar">
|
||||
<div ref={scrollContainerRef} className="flex flex-col">
|
||||
<div
|
||||
ref={setScrollContainerRef}
|
||||
className="transition-all duration-300 ease-in-out overflow-y-auto custom-scrollbar"
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
{displayedConversations.map((conversation) => (
|
||||
<RecentConversation
|
||||
key={conversation.conversation_id}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useCallback } from "react";
|
||||
import { useEffect, useRef, useCallback, useState } from "react";
|
||||
|
||||
interface UseInfiniteScrollOptions {
|
||||
hasNextPage: boolean;
|
||||
@ -14,29 +14,35 @@ export const useInfiniteScroll = ({
|
||||
threshold = 100,
|
||||
}: UseInfiniteScrollOptions) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [container, setContainer] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
// Use a callback ref to track when the container is mounted
|
||||
const setContainerRef = useCallback((node: HTMLDivElement | null) => {
|
||||
containerRef.current = node;
|
||||
setContainer(node);
|
||||
}, []);
|
||||
|
||||
const handleScroll = useCallback(() => {
|
||||
if (!containerRef.current || isFetchingNextPage || !hasNextPage) {
|
||||
if (!container || isFetchingNextPage || !hasNextPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
|
||||
const { scrollTop, scrollHeight, clientHeight } = container;
|
||||
const isNearBottom = scrollTop + clientHeight >= scrollHeight - threshold;
|
||||
|
||||
if (isNearBottom) {
|
||||
fetchNextPage();
|
||||
}
|
||||
}, [hasNextPage, isFetchingNextPage, fetchNextPage, threshold]);
|
||||
}, [container, hasNextPage, isFetchingNextPage, fetchNextPage, threshold]);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return undefined;
|
||||
|
||||
container.addEventListener("scroll", handleScroll);
|
||||
return () => {
|
||||
container.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, [handleScroll]);
|
||||
}, [container, handleScroll]);
|
||||
|
||||
return containerRef;
|
||||
return { ref: setContainerRef, containerRef };
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user