Alec Kan
Alec Kan
Jul 8 · 3 min read

The infinite list (scroll) feature has existed for a while. It’s a great way to enhance your application’s UX.

There are many third-party libraries available, but if you like to challenge yourself as I do, we’ll explore the implementation from scratch using Hooks in this post.

Let’s quick code a simple app component. We initialize an empty state and pass references to the state and setState as props to our InfiniteList component.

The InfiniteList component will return an unordered list of dog images fetched from the Dog API.

Once our request is successful, we push the images to the parent’s state. We’ll make sure not to overwrite the previous state, as we’ll use the getData() function multiple times to fetch more images. So, dot dot dot . . .

So far, this displays the first 15 images.

Now we need to listen for an event when a user scrolls down to the very bottom of the viewport, and fetches more images, calling getData() again.

But how do we determine the bottom reach? I hope this image will help you to understand.

  1. window.innerHeight — static, the height of the user’s browser window.
  2. window.scrollY — dynamic property, current scroll position.
  3. list.clientHeight — static, the height of the container (ul element).
  4. list.offsetTop — container’s indent (if any) from the top of the page, static.

Okay, if 1+2===3+4, we know that a user is at the bottom of the page and we need to fetch.

But what if we want the ul to be a fixed height? We need to get the ul’s properties:

  1. element.scrollHeight — static value, the total scrollable height.
  2. element.scrollTop — dynamic value, the current scroll top position.
  3. element.clientHeight — static value, element’s current height (excluding overflow).

Hence, 1===2+3 will indicate that the user has reached the bottom of the container. And now we can conditionally tell the component whether it’s of fixed height, by setting the scrollable property.

Let’s put this all together in the new useEffect hook.

There’s one last fix to make this all work. Let’s initialize a state, then, once the condition above is met, we toggle the state, giving it a signal to fetch more, by making our first hook dependent on the state’s value.

const [loadMore, setLoadMore] = useState(false);useEffect(() => {
}, [loadMore]);

We can also conditionally render a loading spinner at the bottom while waiting for the API response, by initializing a new state with some truthy value, and then resetting it once the request is fulfilled.

Also, to avoid potential bugs, we could remove the event listener from the window object when the component unmounts, by adding a return statement in useEffect like so:


Live demo | Repo

Thank you for reading and happy coding!

Better Programming

Advice for programmers.

Alec Kan

Written by

Alec Kan

Full-Stack Developer, specializing in JavaScript. Passionate about minimalistic design and creating meaningful projects. Portfolio

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade