DEV Community

Cover image for Maximize User Experience with Infinite Scroll
Tapajyoti Bose
Tapajyoti Bose

Posted on • Edited on

Maximize User Experience with Infinite Scroll

What is Infinite Scroll?

Infinite Scrolling is highly trending as an interaction behavior on pages and lists. The basic functionality is that, as the user scrolls through content, more content is loaded automatically. With the popularity of social media, massive amounts of data are being consumed, Infinite Scrolling offers an efficient way to browse that ocean of information, without having to wait for pages to preload. Rather, the user enjoys a truly responsive experience, whatever device theyโ€™re using.

Demo

Getting Started

We will be implementing Infinite Scroll in a React Project. Set up a React Project before continuing.

Implementing Infinite Scroll

First let's add the Item component.

const Item = ({ children, reference }) => {
  return (
    <div ref={reference}>
      {children}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

We need to use ref to store the reference to the last element for Infinite Scroll.

Creating the App component.

const MAX_PAGES = 5;

const App = () => {
  const [items, setItems] = React.useState([]);
  const [isLoading, setIsLoading] = React.useState(false);
  const [hasMore, setHasMore] = React.useState(true);
  const [pages, setPages] = React.useState(0);
  const observer = React.useRef();

  React.useEffect(() => {
    // generating the 1st page on Component Mounting
    updateItems();
    setPages((pages) => pages + 1);
  }, []);

  const lastItemRef = React.useCallback(
    (node) => {
      // ensuring redundant calls are not made once the data is already BEING updated
      if (isLoading) return;
      if (observer.current) observer.current.disconnect();

      observer.current = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting && hasMore) {
          if (pages < MAX_PAGES) {
            // adding more data only if:
            // 1. MORE data exists ("hasMore" - simulated with "pages < MAX_PAGES")
            // 2. the last item is visible on the screen (using the "IntersectionObserver")
            updateItems();
            setPages((pages) => pages + 1);
          } else {
            setHasMore(false);
          }
        }
      });

      if (node) observer.current.observe(node);
    },
    [isLoading, hasMore]
  );

  const updateItems = async () => {
    setIsLoading(true);
    // simulating asynchronous nature of api (which is the general use case for Infinite Scroll)
    await new Promise((resolve) => setTimeout(resolve, 1000));

    setItems((currItems) => {
      const lastItem = currItems.length;
      const updatedItems = [...currItems];
      for (let i = 1; i <= 5; i++) {
        const item = lastItem + i;
        updatedItems.push(item);
      }
      return updatedItems;
    });

    setIsLoading(false);
  };

  return (
    <React.Fragment>
      <h1>Infinite Scroll Demo</h1>
      {items.map((item, index) =>
        index + 1 === items.length ? (
          <Item reference={lastItemRef} key={index}>
            {item}
          </Item>
        ) : (
          <Item key={index}>
            {item}
          </Item>
        )
      )}
      {isLoading && <div className="loader" />}
    </React.Fragment>
  );
};
Enter fullscreen mode Exit fullscreen mode

Update

As pointed out by some people in the comments, it can lead to performance degradation, so its suggested that when using infinite scroll for large list of items (around 250+ items), you should create the following CSS class:

.hidden {
    visibility: hidden !important;
}
Enter fullscreen mode Exit fullscreen mode

and add it to your items when they are not in the viewport.

Projects using this Implementation

Smartsapp (Previous Messages on scroll up)

Web-app: https://smartsapp-ba40f.firebaseapp.com

GitHub logo ruppysuppy / SmartsApp

๐Ÿ’ฌ๐Ÿ“ฑ An End to End Encrypted Cross Platform messenger app.

Smartsapp

A fully cross-platform messenger app with End to End Encryption (E2EE).

Demo

NOTE: The features shown in the demo is not exhaustive. Only the core features are showcased in the demo.

Platforms Supported

  1. Desktop: Windows, Linux, MacOS
  2. Mobile: Android, iOS
  3. Website: Any device with a browser

Back-end Setup

The back-end of the app is handled by Firebase.

Basic Setup

  1. Go to firebase console and create a new project with the name Smartsapp
  2. Enable Google Analylitics

App Setup

  1. Create an App for the project from the overview page
  2. Copy and paste the configurations in the required location (given in the readme of the respective apps)

Auth Setup

  1. Go to the project Authentication section
  2. Select Sign-in method tab
  3. Enable Email/Password and Google sign in

Firestore Setup

  1. Go to the project Firestore section
  2. Create firestore provisions for the project (choose the server nearest to your location)
  3. Go to the Rulesโ€ฆ

Pizza Man (Previous Orders on scroll down)

Web-app: http://pizza-man-61510.firebaseapp.com/

GitHub logo ruppysuppy / Pizza-Man

๐Ÿ•๐Ÿ›’ An e-commerce website to order pizza online

Pizza Man Project

An E-Commerce website for ordering Pizza Online

Demo

NOTE: The features shown in the demo is not exhaustive. Only the core features are showcased in the demo.

Tools used

  1. React: To create the Single Page App
  2. React-Router: For Routing
  3. Redux: For State Management
  4. Firebase: As a DataBase

Firebase Setup

You need to create a firebase configeration file holding the firebase settings in the path /src/firebase/config.js. The required format is:

const firebaseConfig = {
    apiKey: "API-KEY",
    authDomain: "AUTH-DOMAIN.firebaseapp.com",
    databaseURL: "DATABASE-URL.firebaseio.com",
    projectId: "PROJECT-ID",
    storageBucket: "STORAGE-BUCKET.appspot.com",
    messagingSenderId: "MESSAGING-SENDER-ID",
    appId: "APP-ID",
    measurementId: "MEASUREMENT-ID",
};

export default firebaseConfig;
Enter fullscreen mode Exit fullscreen mode

Data needs to be stored in the following format:

[
    {
        name: "CATEGORY NAME",
        items: [
            {
                desc: "PIZZA DESCRIPTION",
                id: "ID",
                img: "IMAGE LINK",
                name
โ€ฆ
Enter fullscreen mode Exit fullscreen mode

Finding personal finance too intimidating? Checkout my Instagram to become a Dollar Ninja

References

YouTube

  1. Infinite Scrolling With React - Tutorial (Web Dev Simplified)

Smashing Magazine

  1. Infinite Scroll: Letโ€™s Get To The Bottom Of This

Thanks for reading

Reach out to me on:

Top comments (6)

Collapse
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ

This is a lazy, and bad way to implement it. Continually adding stuff to the bottom of the page for an 'infinite' scroll is a sure-fire way to slow the browser down and eat memory. You should also remove items from the top that have scrolled out of view, and readjust accordingly

Collapse
 
ruppysuppy profile image
Tapajyoti Bose • Edited

I totally agree that not removing the items from the top that have scrolled out of view eats away at the memory, but its not feasible in most use cases, especially where the data is dynamic like Facebook (FB doesn't implement this feature either).

Collapse
 
matjones profile image
Mat Jones

FB doesnโ€™t implement this feature either

Yes it does.

Thread Thread
 
ruppysuppy profile image
Tapajyoti Bose

1 page on Facebook:
1 page on Facebook

10 pages on Facebook:
10 pages on Facebook

Collapse
 
matjones profile image
Mat Jones

This is a horrible way to implement infinite scrolling and youโ€™ll get crippling performance issues with a decently large list. You should use something like react-window

Collapse
 
ruppysuppy profile image
Tapajyoti Bose • Edited

I stand rectified, Facebook doesn't un-render the contents, but it does hide them to improve performance