Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

On scroll pagination is not working on big screen

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 ?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

enter image description here

>Solution :

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;
  1. Added a new checkIfMoreContentNeeded function that:
  • Compares viewport height with content height
  • Loads more content if the content doesn’t fill the viewport and there’s more data available
  1. Improved the offset management
  2. Added a resize event listener to handle window size changes
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading