DEV Community

Cover image for No hook libraries - Handle infinitely scrolling in ReactJS
Khang Nguyen
Khang Nguyen

Posted on

1

No hook libraries - Handle infinitely scrolling in ReactJS

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

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

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

Here is the implementation in my Pokémon project.

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay