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
- Host: GitHub
- URL: https://github.com/kotarella1110/use-simple-infinite-scroll
- Owner: kotarella1110
- License: mit
- Created: 2020-09-30T07:42:53.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2023-01-17T11:31:21.000Z (over 2 years ago)
- Last Synced: 2024-10-19T11:10:29.534Z (12 months ago)
- Topics: infinite, infinite-scroll, react, react-hooks, scroll, typescript
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/use-simple-infinite-scroll
- Size: 2.3 MB
- Stars: 19
- Watchers: 2
- Forks: 0
- Open Issues: 18
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
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://github.com/kotarella1110/use-simple-infinite-scroll/actions?query=workflow%3ARelease)
[](https://www.npmjs.com/package/use-simple-infinite-scroll)
[](https://www.npmjs.com/package/use-simple-infinite-scroll)
[](https://www.npmjs.com/package/use-simple-infinite-scroll)
[](https://david-dm.org/kotarella1110/use-simple-infinite-scroll)
[](https://github.com/semantic-release/semantic-release)
[](http://commitizen.github.io/cz-cli/)
[](CONTRIBUTING.md)[](#contributors)
## Installation
```sh
npm install use-simple-infinite-scroll
```## Usage
[](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 ? (
- {item.name}
{items.map((item) => (
))}
) : 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}
) : (
- {item.name}
{data &&
data.map((page, i) => (
{page.data.map((item) => (
))}
))}
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)):
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)