I have implemented a on scroll pagination. but this is not working on large screen. for my api i have limit 12, offset and total. for the first time load on the large screen there is enough space for the 12 dreams, so there is no scroll. that's why the pagination is not working there.
here is my sharedDream.tsx file, where i have added this on scroll pagination.
'use client';
import React, { useCallback, useEffect, useState } from 'react';
import { message, Skeleton } 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 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 ?? [];
if (activeTab === 'My Activity') {
setMyOffset(limit);
} else {
setOffset(limit);
}
setSharedDreamsList((prevData) => [...prevData, ...newData]);
if (
response?.total >
(sharedDreamsList?.length || 0) + (newData?.length || 0)
) {
setHasMore(true);
} else {
setHasMore(false);
}
} else {
message.error(response?.message ?? 'Something went wrong');
}
setIsLoading(false);
};
useEffect(() => {
getAllDreams();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
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);
if (activeTab === 'My Activity') {
setMyOffset(limit + offset);
} else {
setOffset(limit + offset);
}
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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 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={Number(index)}
>
<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) => (
// eslint-disable-next-line react/no-array-index-key
<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>
) : (
!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;
as in the image i have total 18 dreams and there is no scroll for the big screen. so the pagination is not working. can anyone help with this ?
on large screens, when all 12 items fit without scrolling, the scroll-based pagination doesn't trigger. Let's modify the code to handle this case by checking if we need to load more content based on viewport size as well as scroll position.
import React, { useCallback, useEffect, useState } from 'react';
import { message, Skeleton } from 'antd';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
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 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 ?? [];
if (activeTab === 'My Activity') {
setMyOffset(limit);
} else {
setOffset(limit);
}
setSharedDreamsList((prevData) => [...prevData, ...newData]);
if (
response?.total >
(sharedDreamsList?.length || 0) + (newData?.length || 0)
) {
setHasMore(true);
} else {
setHasMore(false);
}
} else {
message.error(response?.message ?? 'Something went wrong');
}
setIsLoading(false);
};
const checkIfMoreContentNeeded = useCallback(() => {
// Get the viewport height
const viewportHeight = window.innerHeight;
// Get the content height
const contentElement = document.documentElement;
const contentHeight = contentElement.scrollHeight;
// If content height is less than viewport height and we have more items to load
if (contentHeight <= viewportHeight && hasMore && !isLoadingMore && !isLoading) {
setIsLoadingMore(true);
getAllDreams().finally(() => {
setIsLoadingMore(false);
if (activeTab === 'My Activity') {
setMyOffset((prev) => prev + limit);
} else {
setOffset((prev) => prev + limit);
}
});
}
}, [hasMore, isLoadingMore, isLoading, getAllDreams, activeTab, limit]);
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);
if (activeTab === 'My Activity') {
setMyOffset((prev) => prev + limit);
} else {
setOffset((prev) => prev + limit);
}
});
}
}, [hasMore, isLoadingMore, getAllDreams, activeTab, limit]);
useEffect(() => {
getAllDreams();
}, []);
useEffect(() => {
// Check if we need more content after initial load
checkIfMoreContentNeeded();
// Add scroll listener
window.addEventListener('scroll', handleScroll);
// Add resize listener to check if more content is needed when window is resized
window.addEventListener('resize', checkIfMoreContentNeeded);
return () => {
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', checkIfMoreContentNeeded);
};
}, [handleScroll, checkIfMoreContentNeeded]);
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 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={Number(index)}
>
<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>
) : (
!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;