1

I tried to use react-infinite-scroll-component to get an infinite scrolling effect.

However, the arr is updating but updating as full length array at the end.

And, I want it to update as slicing by 10 (like first 10, then 10, ...) till length of array using that package. So, array should be updated by length 10 only every 5 secs.

Note: I found below two stackoverflow links which was so messing me a lot!!

And, I couldn't understand anything (and they also use class component well I know how to implement it into functional but I believe something is wrong while mutating array!)

Here is the live demo: codesandbox link

Any suggestions, what is the problem?

nuser137
  • 401
  • 2
  • 8
  • 1
    So are you trying to effectively page the results? You could store all the results into a state and memoize a segment of the array based on the scroll positon. – Mr. Polywhirl Jun 20 '23 at 14:14
  • 1
    actually I want to update an array 10 slices every 5 secs. when I scroll, it updates first slice as 10 but after then add full length that I dont want – nuser137 Jun 20 '23 at 14:15
  • 2
    FWIW infinite scroll is usually triggered by a scroll event (ie when the user reaches near to the bottom of the page) rather than a timer. – Andy Jun 20 '23 at 14:23
  • 1
    Hi, @Andy but I tried to use that package to get infinite scrolling effect, and in their sample example they used timer for that effect – nuser137 Jun 20 '23 at 14:27
  • 1
    @nuser137 I had a bit of a play and [came up with this](https://codesandbox.io/s/jovial-tess-6hr85d). – Andy Jun 20 '23 at 15:30
  • 1
    @Andy, Thanks a lottttt!!!! , As I literally want like that with less code so that easy to understand. Once again thank you so much! Actually I was stucked with this since yesterday, but your comments helped me a lot! Thank you. – nuser137 Jun 20 '23 at 16:28

2 Answers2

0

I guess the issue is that the first10 state variable is not being updated correctly inside the setTimeout callback function. I changed a few things in your code. Check if the following works for you as intended:

import { arr } from "./utils";
import InfiniteScroll from "react-infinite-scroll-component";
import { useState, useEffect } from "react";

export default function App() {
  const [isLoading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [first10, setFirst10] = useState(arr.slice(0, 10));

  const fetchMoreData = () => {
    if (arr.length >= 30) {
      setHasMore(false);
      return;
    }
  };

  useEffect(() => {
    fetchMoreData();

    let insertAt = 10;

    const interval = setInterval(() => {
      if (insertAt >= arr.length) {
        clearInterval(interval);
        setHasMore(false);
        return;
      }

      setFirst10((prevFirst10) => {
        const nextSlice = arr.slice(insertAt, insertAt + 10);
        insertAt += 10;
        return [...prevFirst10, ...nextSlice];
      });
    }, 5000);

    return () => clearInterval(interval);
  }, []);

  return (
    <>
      <div className="mt-24"></div>

      <InfiniteScroll
        dataLength={first10.length}
        next={fetchMoreData}
        hasMore={hasMore}
        loader={<h3 className="font-bold text-2xl">Loading...</h3>}
        endMessage={
          <p className="text-base my-4 font-medium text-center">
            <b>Yay! You have seen it all</b>
          </p>
        }
      >
        {first10.map((t) => (
          <li key={t.id} className="mx-4 mt-8">
            {t.name.concat(` ${t.id}`)}
          </li>
        ))}
      </InfiniteScroll>
    </>
  );
}
  • Thank you for your answer! Well, I tried and it is giving me a full array at a time, not by slicing 10 segments every 5 secs :( – nuser137 Jun 20 '23 at 15:16
  • 1
    oh sorry. so yeah, we have to adjust the logic inside the `useEffect`. I updated the code. hope it works :) – It'sNotARat Jun 20 '23 at 15:30
  • Thank you!! I tried and it works! Actually, I was also trying to use that logic but couldn't understand how to implement it. well, I managed to get the desired thing with the help of your answer. anyways, it helped me a lot! – nuser137 Jun 20 '23 at 16:48
0

Try this:

  • I created a DataScroller component that takes the data and a size i.e. 15
  • I modified the arr variabel in utils to dynamically generate 120 records
  • fetchMoreData is now a callback
  • There is an effect to set hasMore upon data changes

Note: I updated react-infinite-scroll-component to 6.1.0 (latest version available as of this post). I was getting a lot of Warning with the version you were using.

App.js

import DataScroller from "./DataScroller";
import { arr } from "./utils";

export default function App() {
  return (
    <>
      <div className="mt-24"></div>
      <DataScroller data={arr} size={15} />
    </>
  );
}

DataScroller.js

import { useCallback, useEffect, useState } from "react";
import InfiniteScroll from "react-infinite-scroll-component";

export default function DataScroller({ data, size }) {
  const [hasMore, setHasMore] = useState(data.length > size);
  const [visibleData, setVisible] = useState(data.slice(0, size));

  const fetchMoreData = useCallback(() => {
    setTimeout(() => {
      setVisible((currentData) => {
        const startIndex = currentData.length;
        const endIndex = Math.min(startIndex + size, data.length);
        return structuredClone(currentData).concat(
          data.slice(startIndex, endIndex)
        );
      });
    }, 1000);
  }, [data, size]);

  useEffect(() => {
    setHasMore(visibleData.length < data.length);
  }, [data, visibleData]);

  return (
    <InfiniteScroll
      dataLength={visibleData.length}
      next={fetchMoreData}
      hasMore={hasMore}
      loader={<h3 className="font-bold text-2xl">Loading...</h3>}
      endMessage={
        <p className="text-base my-4 font-medium text-center">
          <b>Yay! You have seen it all</b>
        </p>
      }
    >
      {visibleData.map((t) => (
        <li key={t.id} className="mx-4 mt-8">
          {" "}
          {t.name.concat(` ${t.id}`)}
        </li>
      ))}
    </InfiniteScroll>
  );
}

utils.js

export const arr = Array.from({ length: 120 }, (_, i) => ({
  id: i + 1,
  name: "div"
}));
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • Thank you for your answer! As I was trying to understand the issue and want a solution with less code. But still, it will help me in the future if I want to do that. – nuser137 Jun 20 '23 at 16:50
  • 1
    @nuser137 It's not more code. I just abstracted the logic of the data scroller into its own component. This is the "React way". Now you an reuse the component wherever you want. You just need to supply the data and the loading size. Less code does not mean better. This demonstrates loading with the minimal amount of code needed. – Mr. Polywhirl Jun 20 '23 at 17:11
  • Yes, you are right! your answer will be more helpful to my some big applications, but right away, I was actually expecting to understand the issue with minimal code. Anyways, thank you for your response. – nuser137 Jun 20 '23 at 17:29