1.1.0 • Published 3 years ago

use-simple-infinite-scroll v1.1.0

Weekly downloads
198
License
MIT
Repository
github
Last release
3 years ago

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];
NameTypeDefaultRequiredDescripttion
canLoadMorebooleanSpecifies if there are more entities to load.
onLoadMore() => voidCalled when the user has scrolled all the way to the end.
rootMarginstring"0px"Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left).
thresholdnumber \| number[]0Either 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):

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

License

MIT © Kotaro Sugawara

1.1.0

3 years ago

1.0.38

4 years ago

1.0.37

4 years ago

1.0.36

4 years ago

1.0.35

4 years ago

1.0.33

4 years ago

1.0.34

4 years ago

1.0.32

4 years ago

1.0.31

4 years ago

1.0.29

4 years ago

1.0.30

4 years ago

1.0.26

4 years ago

1.0.28

4 years ago

1.0.27

4 years ago

1.0.25

4 years ago

1.0.24

4 years ago

1.0.22

4 years ago

1.0.23

4 years ago

1.0.21

4 years ago

1.0.20

4 years ago

1.0.19

4 years ago

1.0.18

4 years ago

1.0.17

4 years ago

1.0.16

4 years ago

1.0.15

4 years ago

1.0.9

4 years ago

1.0.11

4 years ago

1.0.10

4 years ago

1.0.14

4 years ago

1.0.13

4 years ago

1.0.12

4 years ago

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago