Infinite Scroll in Nextjs and typescript
Tags: nextjs, nestjs, webdev, fullstack
Published: 2024-06-19 23:14:39

After a couple of days I finally was able to achieve an infinite scroll on my project. Here is yet another time saving snippet that you can use to achieve the same in a shorter time frame. As usual this is not a tutorial but a quick post to help a fellow developer save time on implementing an infinite scroll. You'll have to edit the code to fit the needs of your project.

I am using nextUI for the frontend components.

First we'll create a dummy user object

```
interface User {
  id: number;
  name: string;
}
```

Then I create a function to generate dummy data to populate the users array with user objects.

```
const generateDummyData = (start: number, count: number): User[] => {
  return Array.from({ length: count }, (_, i) => ({
    id: start + i,
    name: `User ${start + i + 1}`,
  }));
};
```

Time to implement a function called loadMoreUsers to fetch and append more user data as one scrolls down the page. I have simulated an api call time delay of 2 seconds.

```
const loadMoreUsers = async () => {
    if (loading || page >= 10) return;
    setLoading(true);
    await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate 2-second delay
    const newUsers = generateDummyData(page * 10, 10);
    setUsers((prevUsers) => [...prevUsers, ...newUsers]);
    setPage((prevPage) => prevPage + 1);
    setLoading(false);
  };
```

Next step is to use useEffect to load initial user data when the component mounts and to handle scrolling to load more users...

  

```
useEffect(() => {
    if (!initialized.current) {
        initialized.current = true
        loadMoreUsers();
    }
  }, []);

  useEffect(() => {
    const handleScroll = () => {
        if (
            window.innerHeight +
              Math.max(
                window.pageYOffset,
                document.documentElement.scrollTop,
                document.body.scrollTop
              ) >
            document.documentElement.offsetHeight - 100
          ) {
            if (page < 10 && page > 0) { 
                 loadMoreUsers();
              }
          } else {
            return;
          }

    };

```
Lastly we add an event listener for the scrollbar...

```
window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [page, loading]);
```

That's it. Below is the full script...You can save it as a seperate file in the utils or components and simply import it to your project.

```
import { Card, CardBody,Spinner,Button } from '@nextui-org/react';
import { useEffect, useState, useRef } from 'react';

interface User {
  id: number;
  name: string;
}

const generateDummyData = (start: number, count: number): User[] => {
  return Array.from({ length: count }, (_, i) => ({
    id: start + i,
    name: `User ${start + i + 1}`,
  }));
};

const UserListPage = () => {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(0);

  const initialized = useRef(false)


  const loadMoreUsers = async () => {
    if (loading || page >= 10) return;
    setLoading(true);
    await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate 2-second delay
    const newUsers = generateDummyData(page * 10, 10);
    setUsers((prevUsers) => [...prevUsers, ...newUsers]);
    setPage((prevPage) => prevPage + 1);
    setLoading(false);
  };

  useEffect(() => {
    if (!initialized.current) {
        initialized.current = true
        loadMoreUsers();
    }
  }, []);

  useEffect(() => {
    const handleScroll = () => {
        if (
            window.innerHeight +
              Math.max(
                window.pageYOffset,
                document.documentElement.scrollTop,
                document.body.scrollTop
              ) >
            document.documentElement.offsetHeight - 100
          ) {
            if (page < 10 && page > 0) { 
                 loadMoreUsers();
              }
          } else {
            return;
          }

    };

    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [page, loading]);

  return (
    <div>
      {users.map((user) => (
        <Card key={user.id}>
          <CardBody>
            <p>{user.name}</p>
          </CardBody>
        </Card>
      ))}

      
        {loading ? (<Spinner />) : (<Button onClick={loadMoreUsers}>Load more</Button> )}
    </div>
  );
};

export default UserListPage;

```
That's it folks....Now you can proceed to the other interesting parts of your project. Once I implement the api version I shall share as well