Array is not updating using `slice()` by 10 every 5 secs in functional component in reactjs

161 Views Asked by At

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?

2

There are 2 best solutions below

3
On

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>
    </>
  );
}
3
On

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"
}));