Newsworthy Presidential Mistakes

    react-native-infinite-scroll-view

    0.4.5 • Public • Published

    InfiniteScrollView CircleCI

    InfiniteScrollView is a React Native scroll view that notifies you as the scroll offset approaches the bottom. You can instruct it to display a loading indicator while you load more content. This is a common design in feeds. InfiniteScrollView also supports horizontal scroll views.

    It conforms to ScrollableMixin so you can compose it with other scrollable components.

    npm package

    Installation

    npm install --save react-native-infinite-scroll-view

    Usage

    Compose InfiniteScrollView with the scrollable component that you would like to get events from. In the case of a basic ListView, you would write:

    import React from 'react';
    import {
      ListView,
    } from 'react-native';
    import InfiniteScrollView from 'react-native-infinite-scroll-view';
     
    class ExampleComponent extends React.Component {
      _loadMoreContentAsync = async () => {
        // Fetch more data here.
        // After fetching data, you should update your ListView data source
        // manually.
        // This function does not have a return value.
      }
     
      render() {
        return (
          <ListView
            renderScrollComponent={props => <InfiniteScrollView {...props} />}
            dataSource={...}
            renderRow={...}
            canLoadMore={this.state.canLoadMoreContent}
            onLoadMoreAsync={this._loadMoreContentAsync}
          />
        );
      }
    }

    A more complete example that uses a ListView.DataSource, react-redux, and supports pagination would look something like this:

    import React from 'react';
    import {
      ListView,
      RefreshControl,
    } from 'react-native';
    import InfiniteScrollView from 'react-native-infinite-scroll-view';
    import { connect } from 'react-redux';
     
    class ExampleComponent extends React.Component {
      static propTypes = {
        // Assume data shape looks like:
        // {items: ["item1", "item2"], nextUrl: null, isFetching: false}
        listData: PropTypes.object.isRequired,
     
        // dispatch is automatically provided by react-redux, and is used to
        // interact with the store.
        dispatch: PropTypes.func.isRequired,
      };
     
      constructor(props, context) {
        super(props, context);
     
        this.state = {
          dataSource: new ListView.DataSource({
            rowHasChanged: this._rowHasChanged.bind(this),
          }),
        };
     
        // Update the data store with initial data.
        this.state.dataSource = this.getUpdatedDataStore(props);
      }
     
      async componentWillMount() {
        // Initial fetch for data, assuming that listData is not yet populated.
        this._loadMoreContentAsync();
      }
     
      componentWillReceiveProps(nextProps) {
        // Trigger a re-render when receiving new props (when redux has more data).
        this.setState({
          dataSource: this.getUpdatedDataSource(nextProps),
        });
      }
     
      getUpdatedDataSource(props) {
        // See the ListView.DataSource documentation for more information on
        // how to properly structure your data depending on your use case.
        let rows = props.listData.items;
     
        let ids = rows.map((obj, index) => index);
     
        return this.state.dataSource.cloneWithRows(rows, ids);
      }
     
      _rowHasChanged(r1, r2) {
        // You might want to use a different comparison mechanism for performance.
        return JSON.stringify(r1) !== JSON.stringify(r2);
      }
     
      _renderRefreshControl() {
        // Reload all data
        return (
          <RefreshControl
            refreshing={this.props.listData.isFetching}
            onRefresh={this._loadMoreContentAsync.bind(this)}
          />
        );
      }
     
      _loadMoreContentAsync = async () => {
        // In this example, we're assuming cursor-based pagination, where any
        // additional data can be accessed at this.props.listData.nextUrl.
        //
        // If nextUrl is set, that means there is more data. If nextUrl is unset,
        // then there is no existing data, and you should fetch from scratch.
        this.props.dispatch(fetchMoreContent(this.props.listData.nextUrl));
      }
     
      render() {
        return (
          <ListView
            renderScrollComponent={props => <InfiniteScrollView {...props} />}
            dataSource={this.state.dataSource}
            renderRow={...}
            refreshControl={this._renderRefreshControl()}
            canLoadMore={!!this.props.listData.nextUrl}
            onLoadMoreAsync={this._loadMoreContentAsync.bind(this)}
          />
        );
      }
    }
     
    const mapStateToProps = (state) => {
      return {listData: state.listData};
    };
     
    export default connect(mapStateToProps)(ExampleComponent);

    Tips and Caveats

    • Horizontal scroll views are supported
    • When you load more content in an infinite ListView, the ListView by default will render only one row per frame. This means that for a short amount of time after loading new content, the user could still be very close to the bottom of the scroll view and may trigger a second load.
    • Known issue: Make sure your initial data reaches the bottom of the screen, otherwise scroll events won't trigger. Subsequent loads are not affected. See expo/react-native-infinite-scroll-view#9 for more details.

    Implementation

    InfiniteScrollView uses the onScroll event to continuously calculate how far the scroll offset is from the bottom.

    Install

    npm i react-native-infinite-scroll-view

    DownloadsWeekly Downloads

    618

    Version

    0.4.5

    License

    MIT

    Unpacked Size

    55.8 kB

    Total Files

    8

    Last publish

    Collaborators

    • ide
    • ccheever
    • exponent