react-infinite-scroll-component

repository·master·Indexed 25 days ago

https://github.com/ankeetmaini/react-infinite-scroll-component

A lightweight, zero-dependency React library for implementing infinite scrolling. It features an IntersectionObserver-based approach and supports window scroll, fixed-height containers, and custom scrollable parents. The library includes a high-level InfiniteScroll component and a low-level useInfiniteScroll hook, with built-in support for pull-to-refresh and inverse (chat-style) scrolling.

Tokens
7.8K
Snippets
13
Records
23
Agent score
81%

What's inside react-infinite-scroll-component

  1. Overview of react-infinite-scroll-component APIs

    master

    The library provides two primary ways to implement infinite scrolling:

    1. InfiniteScroll component: The recommended approach for most cases. It handles the loader, endMessage, pull-to-refresh, and inverse scroll UI automatically.
    2. useInfiniteScroll hook: Use this when you need full control over your markup and own the HTML structure. The hook manages the IntersectionObserver and provides a sentinel ref.
  2. Scroll inside a fixed-height container

    master

    To scroll within a specific container instead of the window, use the scrollableTarget prop. You can pass the id of the container as a string or pass a ref directly.

    // Using an ID
    <div id="scrollableDiv" style={{ height: 400, overflow: 'auto' }}>
      <InfiniteScroll
        dataLength={items.length}
        next={fetchMore}
        hasMore={hasMore}
        loader={<p>Loading...</p>}
        scrollableTarget="scrollableDiv"
      >
        {items.map((item) => (
          <div key={item.id}>{item.name}</div>
        ))}
      </InfiniteScroll>
    </div>
    
    // Using a Ref
    const containerRef = useRef<HTMLDivElement>(null);
    
    <div ref={containerRef} style={{ height: 400, overflow: 'auto' }}>
      <InfiniteScroll
        dataLength={items.length}
        next={fetchMore}
        hasMore={hasMore}
        loader={<p>Loading...</p>}
        scrollableTarget={containerRef.current}
      >
        {items.map((item) => (
          <div key={item.id}>{item.name}</div>
        ))}
      </InfiniteScroll>
    </div>
  3. Use the InfiniteScroll component

    master

    The InfiniteScroll component provides infinite scrolling and pull-to-refresh functionality. It can be used in three ways depending on your layout:

    1. Fixed Height: Provide a height prop to make the component itself the scrollable container.
    2. Parent Scroll: Provide a scrollableTarget prop (referencing a parent DOM element) if a parent element is already providing overflow scrollbars.
    3. Window Scroll: If neither height nor scrollableTarget is provided, the component will scroll with the document.body (similar to a Facebook timeline).
    <InfiniteScroll
      pullDownToRefresh
      pullDownToRefreshContent={
        <h3 style={{textAlign: 'center'}}>&#8595; Pull down to refresh</h3>
      }
      releaseToRefreshContent={
        <h3 style={{textAlign: 'center'}}>&#8593; Release to refresh</h3>
      }
      refreshFunction={this.refresh}
      next={fetchData}
      hasMore={true}
      loader={<h4>Loading...</h4>}
      endMessage={
        <p style={{textAlign: 'center'}}>
          <b>Yay! You have seen it all</b>
        </p>
      }>
      {items}
    </InfiniteScroll>
  4. Implement accessibility for `InfiniteScroll`

    master

    To ensure screen readers correctly announce the scroll container and its content, provide a role and an accessible label using aria-label or aria-labelledby.

    Common roles include:

    • "list": For standard item lists.
    • "feed": For activity streams.
    <InfiniteScroll
      role="list"
      aria-label="Search results"
      dataLength={items.length}
      next={fetchMore}
      hasMore={hasMore}
      loader={<p>Loading...</p>}
    >
      {items.map((item) => (
        <div role="listitem" key={item.id}>
          {item.name}
        </div>
      ))}
    </InfiniteScroll>
  5. Implement Pull-to-Refresh

    master

    Enable pull-to-refresh functionality by adding the pullDownToRefresh prop. You can customize the threshold, the refresh function, and the content displayed during the pull actions.

    <InfiniteScroll
      dataLength={items.length}
      next={fetchMore}
      hasMore={hasMore}
      loader={<p>Loading...</p>}
      pullDownToRefresh
      pullDownToRefreshThreshold={50}
      refreshFunction={refreshList}
      pullDownToRefreshContent={
        <h3 style={{ textAlign: 'center' }}>&#8595; Pull down to refresh</h3>
      }
      releaseToRefreshContent={
        <h3 style={{ textAlign: 'center' }}>&#8593; Release to refresh</h3>
      }
    >
      {items.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
    </InfiniteScroll>
  6. Implement Inverse Scroll (Chat/Messaging UIs)

    master

    For chat interfaces where new messages appear at the bottom and you load older messages by scrolling up, use the inverse prop. You should also set the style on the InfiniteScroll component to match the container's flex direction (e.g., flexDirection: 'column-reverse').

    <div
      id="chatBox"
      style={{
        height: 500,
        overflow: 'auto',
        display: 'flex',
        flexDirection: 'column-reverse',
      }}
    >
      <InfiniteScroll
        dataLength={messages.length}
        next={loadOlderMessages}
        hasMore={hasMore}
        loader={<p>Loading older messages...</p>}
        inverse={true}
        scrollableTarget="chatBox"
        style={{ display: 'flex', flexDirection: 'column-reverse' }}
      >
        {messages.map((msg) => (
          <div key={msg.id}>{msg.text}</div>
        ))}
      </InfiniteScroll>
    </div>
  7. Configure the `InfiniteScroll` component

    master

    The InfiniteScroll component is the primary API for implementing infinite scrolling. It requires dataLength, next, hasMore, and loader props.

    Key configuration options include:

    • height: Creates a fixed-height scroll container. Omit to scroll the window.
    • scrollableTarget: The ancestor providing scrollbars. Pass an id string or HTMLElement reference. Required if not scrolling the window or using the height prop.
    • scrollThreshold: How close to the bottom to trigger next() (e.g., 0.8 or '200px').
    • inverse: Enables reverse scroll (e.g., for chat UIs). Use with flexDirection: column-reverse on the container.
    • pullDownToRefresh: Enables pull-to-refresh. Requires refreshFunction.
    • endMessage: Content to show when hasMore is false.
    <InfiniteScroll
      dataLength={items.length}
      next={fetchMore}
      hasMore={hasMore}
      loader={<p>Loading...</p>}
      endMessage={<p>No more items to load.</p>}
    >
      {items.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
    </InfiniteScroll>
  8. Use the `useInfiniteScroll` hook

    master

    The useInfiniteScroll hook provides a low-level API for building fully custom infinite scroll UIs. It returns { sentinelRef, isLoading }.

    Required props:

    • dataLength: Current count of rendered items.
    • next: Callback to fetch more data.
    • hasMore: Boolean indicating if more data is available.

    Optional props:

    • scrollThreshold: Distance from edge to trigger next() (default 0.8).
    • scrollableTarget: The scrollable ancestor (DOM id or HTMLElement).
    • inverse: If true, applies rootMargin to the top edge instead of the bottom.
  9. Use the InfiniteScroll component

    master

    The InfiniteScroll component is the standard way to implement infinite scrolling. It requires dataLength, next (the function to call for more data), and hasMore (a boolean indicating if more data is available). You can also provide a loader component and an endMessage.

    import { useState } from 'react';
    import InfiniteScroll from 'react-infinite-scroll-component';
    
    type Item = { id: number; name: string };
    
    function Feed() {
      const [items, setItems] = useState<Item[]>(initialItems);
      const [hasMore, setHasMore] = useState(true);
    
      const fetchMore = async () => {
        const next = await api.getItems({ offset: items.length });
        if (next.length === 0) {
          setHasMore(false);
          return;
        }
        setItems((prev) => [...prev, ...next]);
      };
    
      return (
        <InfiniteScroll
          dataLength={items.length}
          next={fetchMore}
          hasMore={hasMore}
          loader={<p>Loading...</p>}
          endMessage={<p style={{ textAlign: 'center' }}>All items loaded.</p>}
        >
          {items.map((item) => (
            <div key={item.id}>{item.name}</div>
          ))}
        </InfiniteScroll>
      );
    }
  10. Use the useInfiniteScroll hook

    master

    The useInfiniteScroll hook is for custom UI implementations where you own the markup. It returns a sentinelRef which you must attach to a div at the end of your list. The next function is triggered when this sentinel enters the viewport.

    import { useState } from 'react';
    import { useInfiniteScroll } from 'react-infinite-scroll-component';
    
    type Item = { id: number; name: string };
    
    function CustomFeed() {
      const [items, setItems] = useState<Item[]>(initialItems);
      const [hasMore, setHasMore] = useState(true);
    
      const { sentinelRef, isLoading } = useInfiniteScroll({
        next: async () => {
          const more = await api.getItems({ offset: items.length });
          if (more.length === 0) {
            setHasMore(false);
            return;
          }
          setItems((prev) => [...prev, ...more]);
        },
        hasMore,
        dataLength: items.length,
      });
    
      return (
        <ul>
          {items.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
          <li ref={sentinelRef} aria-hidden="true" />
          {isLoading && <li>Loading...</li>}
          {!hasMore && <li>All items loaded.</li>}
        </ul>
      );
    }