No hook libraries - Handle infinitely scrolling in ReactJS
Tags: webdev, react, beginners, ux
Published: 2024-07-06 08:58:17

While searching ways to implement infinitely scrolling effect on the internet, I realized that many tutorials use additional libraries or hooks to handle this effect 🧐. 

After trying several times, I mostly got it in a quite simple way. So, I decided to write this article to share with you how to implement the infinite scrolling effect in ReactJS without any additional hooks or libraries.

![Demo for infinitely scrolling effect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bcqkh73gzj6ffvvc643n.gif)



## Idea

Before we jump into coding, we need to understand the concept to archive our goal. When the content of the page is longer than client view, our browser will allow us to scroll the client view to see the remaining content.

To create the effect, we just need to calculate the client view (viewport) position when user scrolls. If it reach the threshold, we trigger a fetching action to get more data. Thanks to the available web APIs, we can easily get the position from `window` object.

![Demonstrate idea](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rkg45vq78mmlheij4v97.png)

## Coding

To simple, we can define a threshold about 300px, 100px, any number you want, or you can use the height of the last item as the threshold. 

We use `window.scrollY` to detect the position of the client view (viewport), `document.body.scrollHeight` for the scrollable height, and `window.innerHeight` for the client view height. 

Finally, we check the condition every time user scrolls, if the condition is satisfied, we call API to get more data.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5hiwzjdf9je3pg7ow9i.png)

Here is the implementation in my Pokémon project.

```tsx
export function PokemonsPage() {
  const [pokemons, setPokemons] = useState<Pokemon[]>([]);
  const [loading, setLoading] = useState(false);
  const offset = useRef(0);

  const fetchData = async () => {
    setLoading(true);
    try {
      const data = await getPokemons(offset.current);
      const promises = data.results.map(async (result) => {
        const detail = await getPokemonDetailByUrl(result.url);
        return toPokemon(detail);
      });
      const _pokemons = await Promise.all(promises);
      setPokemons((prev) => [...prev, ..._pokemons]);
      if (_pokemons.length === 20) {
        offset.current += 20;
      }
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData();
  }, []);

  useEffect(() => {
    const handleScroll = () => {
      if (loading) return;

      if (
        window.scrollY + window.innerHeight >
        document.body.scrollHeight - 300
      ) {
        fetchData();
      }
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [loading, pokemons]);

  return (
    <div className="p-6" id="view">
      <List data={pokemons} />
    </div>
  );
}
```

And that's it for this article, I hope this simple implemtation can help you in your projects. Thank you for reading 🤩