GithubHelp home page GithubHelp logo

expo / react-native-infinite-scroll-view Goto Github PK

View Code? Open in Web Editor NEW
518.0 28.0 59.0 65 KB

An infinitely scrolling view that notifies you as the scroll offset approaches the bottom

License: MIT License

JavaScript 100.00%

react-native-infinite-scroll-view's Introduction

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.

react-native-infinite-scroll-view's People

Contributors

abhay-joshi-git avatar brentvatne avatar chrissloey avatar ide avatar intellicode avatar kudo avatar sharcoux avatar viix avatar wli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-infinite-scroll-view's Issues

Incremental list view

Is this component the same as incremental list view which Brent discussed at react Europe 2016?

Scrolling broken - Please check the fix

_shouldLoadMore(event) {

  • return !this.state.isLoading &&
  •  !this.props.canLoadMore && <<<<<<<<---------- I think you introduced this issue recently
    
  •  !this.state.isDisplayingError &&
    
  •  this._distanceFromEnd(event) < this.props.distanceToLoadMore;
    
  • }

onLoadMoreAsync and isLoading relationship

is onLoadMoreAsync supposed to load data and setState before it resolves? I ask because looking at the code, isLoading is set to false as soon as onLoadMoreAsync resolves. If onLoadMoreAsync only dispatches some action and resolves immediately, the loading indicator only blinks into existence briefly. Also your _shouldLoadMore method is then more likely to return true and trigger another fetch.

That said, your example in the README shows something that resolves immediately. Could you clarify? Thanks!

A complete example?

Would love if you'd post a complete example with maybe a fetch() that would show how to render the next rows when reaching the bottom of the ListView. It's kind of hard to grasp as a beginner.

onLoadMoreAsync isn't called until the scroll movement even if bottom is visible

Well, this one is more of a feature request.

Say my async load function only fetches 3 rows at a time. And 3 rows are just half of the screen.
For now I need to scroll the rows in order onLoadMoreAsync to be triggered to load more rows. Ideally I'd like it to be triggered while (canLoadMore && the screen isn't full with rows).

Otherwise great project, thanks=)

Possibility for commenting the example code?

I'm a newbie to the React Native world and i was following the example provided in the InfiniteScrollView.js
It would be damn awesome if someone could comment different sections of this code and let the newbies like me, fully understand the whole code. Cheers ๐Ÿ’ฏ

Why does it need to manually calculate when end is reached?

Hi, I was just wondering why you need to manually calculate when the list has reached the bottom, given that ListView has two built-in methods to do that:

  • onEndReached
  • onEndReachedThreshold

I was able to create an infinite scrolling list quite easily just by using those two props on a default ListView, are there any limitations of this approach?

Thanks!

Fires multiple requests

In my case it fires from 3 to 4 requests one after another, even with distanceToLoadMore set to 0. Adding InteractionManager.runAfterInteractions to finally in _loadMoreAsync seems to solve it, but I am not sure if that's the source of the problem.

My async handler returns promise and actually all bits are working pretty nice apart from the issue above.

Suggestion: improve error reporting

If an error occurs during async content loading, the error message looks like:
Unexpected error while loading more content:', { line: 81832, column: 47, sourceURL: 'http://localhost:8081/index.ios.bundle?platform=ios&dev=true&minify=false' }

Such messages are incredibly unhelpful, given that the internal error message is swallowed and the line number is messed up internally by react-native.

The problem is caused by this line:
console.error('Unexpected error while loading more content:', error);

Here, only the content of error will be serialized by console.error, ignoring the prototype chain and thus the value of error.message (defined in Error).

A simple workaround would be to change this line by something like:
console.error('Unexpected error while loading more content:', error.toString(), error);

This is a very small change but it makes the debugging simpler.

Getting bluebird warnings about swallowed promises

With Bluebird 3.x I am getting:

a promise was created in a handler but none were returned from it

that's coming from _loadMoreAsync() function. Not a big deal, but if we can manage to get rid of it, that'd be awesome!

I believe a fix would be to just add return at the end of the function as suggested here http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-created-in-a-handler-but-none-were-returned-from-it. Otherwise, warnings have to be disabled.

ReactComponent is not defined

I am getting this error due to the ReactComponent type annotation in InfiniteScrollView.js:

getScrollResponder(): ReactComponent {
    return this._scrollComponent.getScrollResponder();
}

Where is ReactComponent? Replacing with other type annotation like any or Object works...

Not entirely sure how to use this

Hi, I've found a bunch of these infinite scroll views but they only seem to give examples for ListView.

I am using KeyboardAwareScrollView or 'native-base' "Content" which is pretty much the same component. I would even consider using scroll view, but I'm pretty sure that listview will not work for what I'm trying to do.

<Container style={{flex: 2}}>
	<Content 
		renderScrollComponent={props => <InfiniteScrollView {...props} />}
		canLoadMore={!!(this.props.tv.page < this.props.page)}
		onLoadMoreAsync={this.loadNextPage.bind(this)}
		horizontal={false}
		refreshControl={this._renderRefreshControl()}>
		{ this.props.page < this.props.total_pages
			? (
				<Card style={styles.rightArrowCard}>
					<CardItem style={styles.rightArrowFiller} header/>
					<CardItem cardBody style={styles.rightArrowContainer}>
						<Body style={styles.rightArrowBody}>
							<Button onPress={this.rightPage} style={styles.rightArrowButton}>
								<Icon style={styles.rightArrowIcon} type="Entypo" name="chevron-thin-right"/>	
							</Button>
						</Body>
					</CardItem>
						<CardItem style={styles.rightArrowFiller}/>
				</Card>
	)
		: null 
	}
	{ 
		TV
		? TV
		: null	
	}
	</Content>

load function which dispatches to redux to get data from next page.

loadNextPage = async () => {
		let page = this.props.page;
		page++;
		this.props.changeDiscoverPageTV(page);
	}

render control function

	_renderRefreshControl() {
		// Reload all data 
		return (
			<RefreshControl
			 refreshing={false}
			 onRefresh={this.loadNextPage.bind(this)}
		  />
		);
	 }

inside of the content component is

	const TV = Object.values(this.props.tv).map((series,i) => (
			<TouchableHighlight key={i} onPress={() => this.updateDisplayInfo(i)}>
				<Card style={styles.cardMain}>
					<CardItem style={styles.cardImageContainer} cardBody>
						<Image 
							source={{uri: `https://image.tmdb.org/t/p/w300${series.poster_path}`}} 
							style={{height:300 , flex: 1}}
							resizeMode="contain"
						/>
					</CardItem>
				</Card>
			</TouchableHighlight>
		));

How exactly can I apply the InfiniteScrollView to this? The documentation is not very complete and does not even list the props you can use or examples for anything outside of ListView. Is it possible to use Infinite with anything other than ListView? I've tried implementing this with my current setup and it does not seem to work!

My issue is that it seems to continuously keep loading content no matter where I am on the page, which ends up messing things up a lot!

Basically all I am trying to do is to have it load additional content once so far on the right, and load more content each time the user gets to the end. For whatever reason, it keeps loading more and more data continuously without stopping regardless of wherever I am on the page, no matter if I use this InfiniteScrollView component or if I try to build my own!

logic issue

hi,
line 17 at DefaultLoadingIndicator ,

Platform.OS === 'android' ?
            <ActivityIndicatorIOS /> :
            <ProgressBarAndroid styleAttr="Small" />

maybe is

Platform.OS === 'android' ?
            <ProgressBarAndroid styleAttr="Small" />:
            <ActivityIndicatorIOS /> 

Support infinite scroll in both directions

Currently this component only allows infinite downward (or rightward) scroll. However, some use cases require infinite scroll in both directions. (For instance, a chat app with a search feature that lets you jump to an old message via the search feature and then scroll either forwards or backwards in time from that point.)

Infinite scroll does not work for off-screen elements

If you imagine an instagram or twitter feed where you maybe have 10,000 pixels of elements, and you want to load more as you get to the bottom, well this doesn't work with this library. I've had a look in _distanceFromEnd and because the element is bigger than the scroll view, it automatically triggers a load. In fact, I load 20+ pages instantly without debouncing.

Is this something that might be looked at?

Cheers

Flow errors

I noticed InfiniteScrollView.js includes: /* @flow */ at the top, but it produces a long list of flow errors:

Any reason for that?

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:18
 18:   static propTypes = {
       ^ class property initializers are not yet supported (https://github.com/facebook/flow/issues/850)

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:27
 27:   static defaultProps = {
       ^ class property initializers are not yet supported (https://github.com/facebook/flow/issues/850)

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:37
 37:     return this._scrollComponent.getScrollResponder();
                ^^^^^^^^^^^^^^^^^^^^^ property `_scrollComponent`
 37:     return this._scrollComponent.getScrollResponder();
                     ^^^^^^^^^^^^^^^^ property `_scrollComponent`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:40
 40:   setNativeProps(nativeProps) {
                      ^^^^^^^^^^^ parameter `nativeProps`. Missing annotation

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:41
 41:     this._scrollComponent.setNativeProps(nativeProps);
         ^^^^^^^^^^^^^^^^^^^^^ property `_scrollComponent`
 41:     this._scrollComponent.setNativeProps(nativeProps);
              ^^^^^^^^^^^^^^^^ property `_scrollComponent`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:45
 45:     if (this.props.isLoadingMore) {
             ^^^^^^^^^^ property `props`
 45:     if (this.props.isLoadingMore) {
                  ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:47
 47:         this.props.renderLoadingIndicator(),
             ^^^^^^^^^^ property `props`
 47:         this.props.renderLoadingIndicator(),
                  ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:55
 55:     } = this.props;
             ^^^^^^^^^^ property `props`
 55:     } = this.props;
                  ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:58
 58:       children: [this.props.children, loadingIndicator],
                      ^^^^^^^^^^ property `props`
 58:       children: [this.props.children, loadingIndicator],
                           ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:62
 62:       ref: component => { this._scrollComponent = component; },
                               ^^^^^^^^^^^^^^^^^^^^^ assignment of property `_scrollComponent`
 62:       ref: component => { this._scrollComponent = component; },
                                    ^^^^^^^^^^^^^^^^ property `_scrollComponent`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:66
 66:   _handleScroll(event) {
                     ^^^^^ parameter `event`. Missing annotation

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:67
 67:     if (this.props.onScroll) {
             ^^^^^^^^^^ property `props`
 67:     if (this.props.onScroll) {
                  ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:68
 68:       this.props.onScroll(event);
           ^^^^^^^^^^ property `props`
 68:       this.props.onScroll(event);
                ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:71
 71:     if (this.props.isLoadingMore || !this.props.canLoadMore) {
             ^^^^^^^^^^ property `props`
 71:     if (this.props.isLoadingMore || !this.props.canLoadMore) {
                  ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:71
 71:     if (this.props.isLoadingMore || !this.props.canLoadMore) {
                                          ^^^^^^^^^^ property `props`
 71:     if (this.props.isLoadingMore || !this.props.canLoadMore) {
                                               ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:75
 75:     if (this._distanceFromEnd(event) < this.props.distanceToLoadMore) {
                                            ^^^^^^^^^^ property `props`
 75:     if (this._distanceFromEnd(event) < this.props.distanceToLoadMore) {
                                                 ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:76
 76:       this.props.onLoadMore();
           ^^^^^^^^^^ property `props`
 76:       this.props.onLoadMore();
                ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:80
 80:   _distanceFromEnd(event): number {
                        ^^^^^ parameter `event`. Missing annotation

node_modules/react-native-infinite-scroll-view/InfiniteScrollView.js:88
 88:     if (this.props.horizontal) {
             ^^^^^^^^^^ property `props`
 88:     if (this.props.horizontal) {
                  ^^^^^ property `props`. Property not found in
 17: class InfiniteScrollView {
           ^^^^^^^^^^^^^^^^^^ InfiniteScrollView

scrollTo is not a function

InfiniteScroll doesn't have methods of real scrollView. For example, it doesn't have scrollTo() method. It would be very good, if this wrapper will have methods of its child.

Babel version

npm install gives me the following warning,

$ npm install react-native-infinite-scroll-view
npm WARN unmet dependency /Users/satya/Workspace/Scrollback/HeyNeighbor/node_modules/react-native requires babel@'5.8.21' but will load
npm WARN unmet dependency /Users/satya/Workspace/Scrollback/HeyNeighbor/node_modules/babel,
npm WARN unmet dependency which is version 5.8.23
[email protected] node_modules/react-native-infinite-scroll-view

Complete Example showing all available events and properties ?

Sorry to ask this again ( I saw a couple of closed tickets ), But it's kind of hard to implement the infinite scrollView.

Can someone provide a complete working example of how to use canLoadMore , isLoadingMore and onLoadMoreAsync for the infinite scrolling ?

Including a screenshot would be super helpful.

PS: I am facing another problem with the listView. I was using the contentContainerStyle to show the grid style inside the listView ( as explained here). With this set-up, I am not sure how to implement the infinite scrolling. Can someone lead me to the right direction ?

This is how the listView now:

<ScrollView>
          <ListView
            dataSource={items}
            contentContainerStyle={styles.gridContainer}
            renderRow={(details) => Product(details, this.onPressAction) }
            onEndReachedThreshold={40}
            onEndReached={this.onEndReached}
          />
</ScrollView>

Typescript types

Are there any plans to support typescript offically?
e.g. @types/react-native-infinite-scroll-view

feel free to close this issue if this is not the right place to ask.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.