use-simple-infinite-scroll
TypeScript icon, indicating that this package has built-in type declarations

1.1.0 • Public • Published

use-simple-infinite-scroll

A simple React Hook for infinite scrolling built on the Intersection Observer API

License Actions Status NPM Version Downloads Month Downloads Total Dependencies Status Semantic Release Commitizen Friendly PRs Welcome

All Contributors

Installation

npm install use-simple-infinite-scroll

Usage

Edit kotarella1110/use-simple-infinite-scroll: example

Basic

import React, { useState } from 'react';
import { useSimpleInfiniteScroll } from 'use-simple-infinite-scroll';
 
type Item = {
  id: number;
  name: string;
};
 
type Result = {
  data: Item[];
  nextCursor: number | null;
};
 
const canFetchMore = (nextCursor: Result['nextCursor']) => nextCursor !== null;
 
const InfiniteScrollExample = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [items, setItems] = useState<Item[]>([]);
  const [nextCursor, setNextCursor] = useState<Result['nextCursor']>(0);
 
  const fetchMore = () => {
    setIsLoading(true);
    fetch(`/api/items?cursor=${nextCursor}`)
      .then((res) => res.json())
      .then((res: Result) => {
        setItems([...items, ...res.data]);
        setNextCursor(res.nextCursor);
        setIsLoading(false);
      });
  };
 
  const [targetRef] = useSimpleInfiniteScroll({
    onLoadMore: fetchMore,
    canLoadMore: canFetchMore(nextCursor),
  });
 
  return (
    <>
      {items.length !== 0 ? (
        <ul>
          {items.map((item=> (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      ) : null}
      <div ref={targetRef}>
        {isLoading
          ? 'Loading more...'
          : canFetchMore(nextCursor)
          ? 'Load More'
          : 'Nothing more to load'}
      </div>
    </>
  );
};

React Query

import React from 'react';
import { useInfiniteQuery } from 'react-query';
import { useSimpleInfiniteScroll } from 'use-simple-infinite-scroll';
 
type Item = {
  id: number;
  name: string;
};
 
type Result = {
  data: Item[];
  nextCursor: number | null;
};
 
const InfiniteScrollExample = () => {
  const {
    status,
    data,
    error,
    isFetching,
    isFetchingMore,
    fetchMore,
    canFetchMore,
  } = useInfiniteQuery<Result, Error>(
    'items',
    (key: string, cursor = 0) =>
      fetch(`/api/items?cursor=${cursor}`).then((res) => res.json()),
    {
      getFetchMore: (lastGroup) => lastGroup.nextCursor,
    },
  );
 
  const [targetRef, rootRef] = useSimpleInfiniteScroll({
    onLoadMore: fetchMore,
    canLoadMore: !!canFetchMore,
  });
 
  return status === 'loading' ? (
    <p>Loading...</p>
  ) : status === 'error' ? (
    <span>Error: {error && error.message}</span>
  ) : (
    <div
      style={{
        overflow: 'auto',
      }}
      ref={rootRef}
    >
      <ul>
        {data &&
          data.map((page, i) => (
            <React.Fragment key={i}>
              {page.data.map((item) => (
                <li key={itme.id}>{item.name}</li>
              ))}
            </React.Fragment>
          ))}
      </ul>
      <div>
        <button
          type="button"
          ref={targetRef}
          onClick={() => fetchMore()}
          disabled={!canFetchMore || !!isFetchingMore}
        >
          {isFetchingMore
            ? 'Loading more...'
            : canFetchMore
            ? 'Load More'
            : 'Nothing more to load'}
        </button>
      </div>
      <div>
        {isFetching && !isFetchingMore ? 'Background Updating...' : null}
      </div>
    </div>
  );
};

API

const useSimpleInfiniteScroll: (options: {
  canLoadMore: boolean;
  onLoadMore: () => void;
  rootMargin?: string;
  threshold?: number | number[];
}) => [(target: Element | null) => void, (root: Element | null) => void];
Name Type Default Required Descripttion
canLoadMore boolean Specifies if there are more entities to load.
onLoadMore () => void Called when the user has scrolled all the way to the end.
rootMargin string "0px" Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left).
threshold number \| number[] 0 Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed.

For more information on rootMargin and threshold option, visit the MDN web docs.

FAQ

How can I assign multiple refs to a component?

You can wrap multiple ref assignments in a single useCallback:

import React, { useRef, useCallback } from 'react';
import { useSimpleInfiniteScroll } from 'use-simple-infinite-scroll';
 
const AssignMultipleRefsExample = () => {
  const rootRef = useRef<HTMLDivElement | null>();
  const targetRef = useRef<HTMLDivElement | null>();
 
  const [setTargetRef, setRootRef] = useSimpleInfiniteScroll({
    onLoadMore: () => {},
    canLoadMore: true,
  });
 
  // Use `useCallback` so we don't recreate the function on each render - Otherwise, the function passed to `onLoadMore` option will be called twice
  const setRootRefs = useCallback(
    (node: HTMLDivElement | null) => {
      // Ref's from useRef needs to have the node assigned to `current`
      rootRef.current = node;
      // Callback refs, like the one from `useSimpleInfiniteScroll`, is a function that takes the node as an argument
      setRootRef(node);
    },
    [setRootRef],
  );
 
  const setTargetRefs = useCallback(
    (node: HTMLDivElement | null) => {
      targetRef.current = node;
      setTargetRef(node);
    },
    [setTargetRef],
  );
 
  return (
    <div ref={setRootRefs}>
      <div ref={setTargetRefs} />
    </div>
  );
};

Which browsers are supported?

use-simple-infinite-scroll supports all of the major modern browsers. Browsers like IE11 are not supported: if you need to support older browsers you can add IntersectionObserver polyfill.

You can install the polyfill via npm or by downloading a zip of this repository:

npm install intersection-observer

Then import it in your app:

import 'intersection-observer';

Contributing

Contributions are always welcome! Please read the contributing first.

Contributors

Thanks goes to these wonderful people (emoji key):


Kotaro Sugawara

💻 📖 🤔 🚇 ⚠️

This project follows the all-contributors specification. Contributions of any kind welcome!

License

MIT © Kotaro Sugawara

Readme

Keywords

Package Sidebar

Install

npm i use-simple-infinite-scroll

Weekly Downloads

8

Version

1.1.0

License

MIT

Unpacked Size

27.5 kB

Total Files

16

Last publish

Collaborators

  • kotarella1110