Installation
npm install use-simple-infinite-scroll
Usage
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 ,
} ) ;
const setRootRefs = useCallback (
( node : HTMLDivElement | null ) => {
rootRef . current = node ;
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