An open API service indexing awesome lists of open source software.

https://github.com/kotarella1110/use-simple-infinite-scroll

A simple React Hook for infinite scrolling built on the Intersection Observer API
https://github.com/kotarella1110/use-simple-infinite-scroll

infinite infinite-scroll react react-hooks scroll typescript

Last synced: 6 months ago
JSON representation

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

Awesome Lists containing this project

README

          

use-simple-infinite-scroll

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

[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE)
[![Actions Status](https://github.com/kotarella1110/use-simple-infinite-scroll/workflows/Release/badge.svg)](https://github.com/kotarella1110/use-simple-infinite-scroll/actions?query=workflow%3ARelease)
[![NPM Version](https://img.shields.io/npm/v/use-simple-infinite-scroll?style=flat-square)](https://www.npmjs.com/package/use-simple-infinite-scroll)
[![Downloads Month](https://img.shields.io/npm/dm/use-simple-infinite-scroll?style=flat-square)](https://www.npmjs.com/package/use-simple-infinite-scroll)
[![Downloads Total](https://img.shields.io/npm/dt/use-simple-infinite-scroll?style=flat-square)](https://www.npmjs.com/package/use-simple-infinite-scroll)
[![Dependencies Status](https://david-dm.org/kotarella1110/use-simple-infinite-scroll.svg?style=flat-square)](https://david-dm.org/kotarella1110/use-simple-infinite-scroll)
[![Semantic Release](https://img.shields.io/badge/%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release)
[![Commitizen Friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)](http://commitizen.github.io/cz-cli/)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-green.svg?style=flat-square)](CONTRIBUTING.md)

[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors)

## Installation

```sh
npm install use-simple-infinite-scroll
```

## Usage

[![Edit kotarella1110/use-simple-infinite-scroll: example](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/kotarella1110/use-simple-infinite-scroll/tree/master/example?fontsize=14&hidenavigation=1&theme=dark)

### Basic

```tsx
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([]);
const [nextCursor, setNextCursor] = useState(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 ? (


    {items.map((item) => (
  • {item.name}

  • ))}

) : null}

{isLoading
? 'Loading more...'
: canFetchMore(nextCursor)
? 'Load More'
: 'Nothing more to load'}

>
);
};
```

### React Query

```tsx
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(
'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' ? (

Loading...


) : status === 'error' ? (
Error: {error && error.message}
) : (


    {data &&
    data.map((page, i) => (

    {page.data.map((item) => (
  • {item.name}

  • ))}

    ))}


fetchMore()}
disabled={!canFetchMore || !!isFetchingMore}
>
{isFetchingMore
? 'Loading more...'
: canFetchMore
? 'Load More'
: 'Nothing more to load'}



{isFetching && !isFetchingMore ? 'Background Updating...' : null}


);
};
```

## API

```ts
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](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).

## FAQ

### How can I assign multiple refs to a component?

You can wrap multiple `ref` assignments in a single `useCallback`:

```tsx
import React, { useRef, useCallback } from 'react';
import { useSimpleInfiniteScroll } from 'use-simple-infinite-scroll';

const AssignMultipleRefsExample = () => {
const rootRef = useRef();
const targetRef = useRef();

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 (




);
};
```

### 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](https://www.npmjs.com/package/intersection-observer).

You can install the polyfill via npm or by downloading a [zip](https://github.com/w3c/IntersectionObserver/archive/gh-pages.zip) of this repository:

```sh
npm install intersection-observer
```

Then import it in your app:

```js
import 'intersection-observer';
```

## Contributing

Contributions are always welcome! Please read the [contributing](./CONTRIBUTING.md) first.

## Contributors

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):



Kotaro Sugawara

💻 📖 🤔 🚇 ⚠️

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

## License

[MIT](./LICENSE) © [Kotaro Sugawara](https://twitter.com/kotarella1110)