大屏环境下On Scroll Pagination失效问题求助
解决大屏滚动分页无法触发的问题
你的核心问题是:大屏设备上首次加载的12条内容刚好填满视口,页面无滚动条,导致基于scroll事件的分页逻辑无法触发,剩余6条数据无法加载。以下是具体修复方案:
1. 初始化后主动检查是否需要加载更多
在首次数据加载完成后,判断内容高度是否小于视口高度,同时还有剩余数据未加载,直接触发一次加载逻辑。
2. 修复offset更新的逻辑错误
原代码中更新myOffset和offset时存在变量混用问题,比如My Activity标签下错误使用了offset变量,需要修正。
3. (可选)添加兜底的"加载更多"按钮
为极端场景(比如内容始终无法填满视口)提供手动触发入口。
修改后的完整代码
'use client'; import React, { useCallback, useEffect, useState, useRef } from 'react'; import { message, Skeleton, Button } from 'antd'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; import NoDreamIcon from '@/assests/svg/NoDreamIcon'; import { useAppSelector } from '@/hooks/reduxHooks'; import { selectpagename } from '@/redux/slice/pageTitleSlice'; import { fetchAllMyActivityDreams } from '@/service/shared-dreams/my-activity'; import { fetchAllSharedDreams } from '@/service/shared-dreams/shared-dreams'; import { IDreamDetails } from '../../../@types/common'; import testImage from '../../../public/shared-dream.png'; const SharedDreams = () => { const router = useRouter(); const { activeTab } = useAppSelector(selectpagename); const [sharedDreamsList, setSharedDreamsList] = useState<IDreamDetails[]>([]); const [isLoading, setIsLoading] = useState<boolean>(false); const limit = 12; const [offset, setOffset] = useState(0); const [hasMore, setHasMore] = useState<boolean>(false); const [myOffset, setMyOffset] = useState(0); const [isLoadingMore, setIsLoadingMore] = useState<boolean>(false); const contentRef = useRef<HTMLDivElement>(null); // 监听内容容器高度 const getAllDreams = async () => { setIsLoading(true); const response = activeTab === 'My Activity' ? await fetchAllMyActivityDreams(limit, myOffset) : await fetchAllSharedDreams(limit, offset, ''); if (response?.status === 1) { const newData = response?.data ?? []; // 修正offset更新逻辑,使用函数式更新避免闭包问题 if (activeTab === 'My Activity') { setMyOffset(prev => prev + limit); } else { setOffset(prev => prev + limit); } setSharedDreamsList((prevData) => [...prevData, ...newData]); // 正确计算剩余数据量 const totalLoaded = (sharedDreamsList?.length || 0) + (newData?.length || 0); setHasMore(response?.total > totalLoaded); } else { message.error(response?.message ?? 'Something went wrong'); } setIsLoading(false); }; useEffect(() => { getAllDreams(); }, []); // 新增:检查内容高度是否不足视口,主动触发加载 useEffect(() => { if (!isLoading && hasMore && contentRef.current) { const contentHeight = contentRef.current.offsetHeight; const viewportHeight = window.innerHeight; // 内容高度小于视口高度,且还有数据未加载,直接加载更多 if (contentHeight < viewportHeight) { setIsLoadingMore(true); getAllDreams().finally(() => setIsLoadingMore(false)); } } }, [isLoading, hasMore]); const handleScroll = useCallback(() => { const scrollPosition = window.scrollY + window.innerHeight; const docElement = document.documentElement; const { scrollHeight } = docElement ?? {}; if ( scrollHeight && scrollPosition >= scrollHeight - 1 && !isLoadingMore && hasMore ) { setIsLoadingMore(true); getAllDreams().finally(() => setIsLoadingMore(false)); } }, [hasMore, isLoadingMore]); useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [handleScroll]); const handleClick = (id: string) => { router.push(`/shared-dreams/${id}`); }; return ( <div className='flex h-full w-full flex-col gap-4 text-white'> <h1 className='text-base font-bold'> {activeTab === 'My Activity' ? 'Shared Dreams I’m in' : 'All Shared Dreams'} </h1> {sharedDreamsList?.length > 0 || isLoading ? ( <> <div ref={contentRef} // 绑定内容容器ref className='grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4' > {sharedDreamsList?.map((dream: IDreamDetails, index: number) => { const { thumbnail, title, total_member: totalMember, is_accessible: isAccessible = true } = dream || {}; return ( <div className={`flex flex-col gap-2 ${!isAccessible && 'cursor-not-allowed'}`} onClick={() => isAccessible && handleClick(dream?.id)} role='button' tabIndex={0} key={dream.id || index} // 用dream.id作为key更合理 > <Image src={ thumbnail ? `${thumbnail?.base_url}${thumbnail?.internal_path}${thumbnail?.image}` : testImage } alt={`Dream ${index + 1}`} className={`aspect-[4/3] h-[180px] w-full rounded-lg object-cover ${!isAccessible && 'opacity-20'}`} height={122} width={260} /> <div className='flex flex-col gap-1'> <p className='truncate text-base font-semibold'>{title}</p> <p> <span className='font-bold'>{totalMember}</span> {' '} <span className='text-neutrals-300'>Members</span> </p> </div> </div> ); })} {(isLoading || isLoadingMore) && Array.from({ length: 8 }).map((_, index) => ( <div className='flex flex-col gap-2' key={index}> <Skeleton.Image active rootClassName='skeleton-img skeleton-wrapper' /> <Skeleton active paragraph={{ rows: 1 }} rootClassName='skeleton-wrapper skeleton-card' /> </div> ))} </div> {/* 新增:加载更多按钮作为兜底 */} {hasMore && !isLoading && !isLoadingMore && ( <div className='flex justify-center mt-4'> <Button onClick={() => { setIsLoadingMore(true); getAllDreams().finally(() => setIsLoadingMore(false)); }} loading={isLoadingMore} > 加载更多 </Button> </div> )} </> ) : ( !isLoading && sharedDreamsList?.length === 0 && ( <div className='flex h-full flex-col items-center justify-center self-stretch'> <div className='flex flex-col items-center justify-center gap-3 self-stretch text-center max-sm:h-[77vh]'> <NoDreamIcon /> <div className='inline-flex flex-col items-center justify-center gap-1'> <p className='text-lg font-bold leading-[27px] text-[#f6f6fd]'> No Dreams Joined Yet </p> <p className='self-stretch text-center text-sm font-medium leading-[21px] text-[#9090af]'> {`It looks like you haven't joined any community dreams yet.`} </p> </div> </div> </div> ) )} </div> ); }; export default SharedDreams;
关键修复点说明
- 主动检查内容高度:通过
useRef获取内容容器高度,在数据加载完成后对比视口高度,若内容未填满且有剩余数据,直接触发加载。 - 修正offset更新:使用函数式更新
setMyOffset(prev => prev + limit)避免闭包问题,同时确保My Activity标签下使用正确的myOffset变量。 - 添加加载更多按钮:作为scroll事件的兜底,确保极端场景下用户仍能加载剩余数据。
- 优化key值:用
dream.id替代索引作为key,提升列表渲染性能。
内容的提问来源于stack exchange,提问作者Brijal Kansara
相关产品推荐
相关产品推荐

