Skip to content
A brutally simple react infinite scroll component
Branch: master
Clone or download
jaredpalmer Merge pull request #5 from jaredpalmer/jaredpalmer-patch-1
Update readme with proper TS signatures
Latest commit 149e801 Sep 28, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src add snapshot tests for render and component Sep 16, 2018
.gitignore init covfefe May 31, 2017
LICENSE init covfefe May 31, 2017
README.md Update README.md Sep 27, 2018
package.json bump version Sep 27, 2018
tsconfig.json init covfefe May 31, 2017
tslint.json init covfefe May 31, 2017
webpack.lib.js init covfefe May 31, 2017
webpack.umd.js Change name to infinite-scroll May 31, 2017
yarn.lock run yarn upgrade to update lock files Sep 22, 2018

README.md

React Simple Infinite Scroll

A brutally simple infinite scroll helper component.

Install

npm install react-simple-infinite-scroll --save

Usage

This component uses a "sentinel" div (with a React ref) that calls getBoundingClientRect() to measure its position and fire off a callback when it becomes visible again. Note: this package eventually becomes somewhat inefficient on very very very large lists because it keeps adding nodes to the DOM. However, this package is extremely valuable for situations when a windowing technique is not possible and when a user is going to realistically scroll a few hundred rows (and not thousands of rows).

Why not use windowing (react-virtualized)?

If you can, you probably should. However, windowing only works when you know the total number of items in the result set ahead of time. This isn't always possible. For example, let's say you have a MongoDB database where you cannot efficiently return the total number of documents in a given query. All your API returns is a cursor (so you can know is if there is another page or not). While this would prevent you from using windowing/react-virtualized, react-simple-infinite-scroll will work just fine.

The Gist

<InfiniteScroll
  throttle={100}
  threshold={300}
  isLoading={this.state.isLoading}
  hasMore={!!this.state.cursor}
  onLoadMore={this.loadMore}
>
  {this.state.myArrayOfItems.map(item => <div>{/* ... */}</div>)}
</InfiniteScroll>
<InfiniteScroll
  throttle={100}
  threshold={300}
  isLoading={this.state.isLoading}
  hasMore={!!this.state.cursor}
  onLoadMore={this.loadMore}
  render={({ sentinel }) => (
    <section>
      {this.state.myArrayOfItems.map(item => <div>{/* ... */}</div>)}
      {sentinel}
    </section>
  )}
/>
// Small react-redux pseudocode
// `storeData` is information extracted from the store
const MyComponent = ({ sentinel, storeData }) => (
  <section>
    {storeData.entities}
    {sentinel}
  </section>
);

const ConnectedComponent = connect(/* ... */)(MyComponent);

<InfiniteScroll
  throttle={100}
  threshold={300}
  isLoading={storeData.isLoading}
  hasMore={storeData.hasMore}
  onLoadMore={() => boundActions.fetchMoreEntities(storeData.cursor)}
  component={ConnectedComponent}
/>

Full Example

import React from 'react'
import InfiniteScroll from 'react-simple-infinite-scroll'

export class MyInfiniteScrollExample extends React.Component {
  state = {
    items: [],
    isLoading: true,
    cursor: 0
  }

  componentDidMount() {
    // do some paginated fetch
    this.loadMore()
  }

  loadMore = () => {
    this.setState({ isLoading: true, error: undefined })
    fetch(`https://api.example.com/v1/items?from=${this.state.cursor}`)
      .then(res => res.json())
      .then(
        res => {
          this.setState(state => ({
            items: [...state.items, ...res.items],
            cursor: res.cursor,
            isLoading: false
          }))
        },
        error => {
          this.setState({ isLoading: false, error })
        }
    )
  }

  render() {
    return (
      <div>
        <InfiniteScroll
          throttle={100}
          threshold={300}
          isLoading={this.state.isLoading}
          hasMore={!!this.state.cursor}
          onLoadMore={this.loadMore}
        >
          {this.state.items.length > 0
            ? this.state.items.map(item => (
                <MysListItem key={item.id} title={item.title} />
              ))
            : null}
        </InfiniteScroll>
        {this.state.isLoading && (
          <MyLoadingState />
        )}
      </div>
    )
  }
}

API Reference

Props

hasMore: boolean

Required

Specifies if there are more entities to load.

isLoading: boolean

Required

When true, onLoadMore() will not be executed on scroll.

onLoadMore: () => void

Required

Called when the user has scrolled all the way to the end. This happens when the sentinel has reached the threshold.

threshold?: number

Scroll threshold. Number of pixels before the sentinel reaches the viewport to trigger onLoadMore().

throttle?: number = 64

Defaults to 64. Scroll handler will be executed at most once per the number of milliseconds specified.

Warning: Making this number closer to zero can decrease performance due to a force reflow caused by getBoundingClientRect(), see more properties that can cause this issue in this gist by Paul Irish.

render?: (props: ScrollProps) => React.ReactNode

Callback used for convenient inline rendering and wrapping. Arguments passed Object: { sentinel, children }. Use this if you have a more complex layout where the sentinel needs to be injected.

Warning: The sentinel must be rendered (injected into the DOM) in order for this library to work properly, failing to do so will result in errors and unexpected side effects.

component?: React.ComponentType<ScrollProps>

React component. Similar to the render() prop, this component will receive Object: { sentinel, children } as props. Note that render() prop has precedence over this property, meaning that if both are present, component will not be rendered.

Warning: The sentinel must be rendered (injected into the DOM) in order for this library to work properly, failing to do so will result in errors and unexpected side effects.

Alternatives

Author

Contributors

jared palmer
jared palmer
💻 📖 💡
pablo garcia
pablo garcia
💻 📖 💡

This project follows the all-contributors specification.

You can’t perform that action at this time.