Nativebase: onEndReached not working as should

Created on 23 Feb 2017  路  9Comments  路  Source: GeekyAnts/NativeBase

Hi, onEndReached in List is triggered on start of the list

react-native, react and native-base version

"react-native": "0.41.2"
"react": "~15.4.0"
"native-base": "^2.0.9"

Expected behaviour

It should be excuted, only when the end of the list is reached or the height in pixels from the inside container of the listviews bottom, specified in https://facebook.github.io/react-native/docs/listview.html#onendreachedthreshold

Actual behaviour

It executed when list is loaded two times.

Steps to reproduce (code snippet or screenshot)

import React, {Component, PropTypes, AppRegistry } from 'react';
import {
  StyleSheet,
  Dimensions,
 TextInput
} from 'react-native';
import {Dimensions, Container, Content, List, Grid, Row, Button, Icon, Header, Footer, FooterTab, Text } from 'native-base';
import VideoPlayer from './videoPlayer';

const deviceWidth = Dimensions.get('window').width;

const videoHeight = deviceWidth + 10;

const styleVars = {
  container: {
    backgroundColor: '#ffffff',
  },
  videoList: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'stretch',
  },
  ListViewStyle: {
    flex: 0,
    width: deviceWidth,
    backgroundColor: 'transparent',
    justifyContent: 'center',
    alignItems: 'stretch',
    margin: 0,
    padding: 0,
    borderWidth: 2,
  },
  inputStyle: {
    minWidth: 100,
    alignSelf: 'stretch',
    padding: 0,
    margin: 0,
    backgroundColor: 'transparent',
    color: 'black',
    textDecorationLine: 'none',
  },
 item: {
    position: 'relative',
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 1,
    elevation: 1,
    flex: 1,
    backgroundColor: 'transparent',
    height: Dimensions.get('window').width,
}
};

const styles = StyleSheet.create(styleVars);

class App extends Component {
  static propTypes = {
    videos: PropTypes.array.isRequired,
    hashtags: PropTypes.array,
  };

  constructor(props) {
    super(props);

    this.state = {
      offsetVideosCount: 0,
      dataSource: this.props.videos,
      items: [
          { title: '1' },
          { title: '2' },
          { title: '3' },
          { title: '4' },
          { title: '5' },
          { title: '6' },
          { title: '7' },
          { title: '8' },
          { title: '9' },
          { title: '10' },
          { title: '11' },
          { title: '12' },
          { title: '13' },
          { title: '14' },
          { title: '15' },
          { title: '16' },
          { title: '17' },
          { title: '18' },
          { title: '19' },
       ],
    };
  }

  renderRow(item, sectionID, rowID) {
    return (
      <View style={styles.video}>
          <Text>{sectionID}-{rowID}</Text>
      </View>
    );
  }

  whatsOnScroll(e) {
    const offsetVideosCountRound = Number(Math.round(e.nativeEvent.contentOffset.y / videoHeight));
    const offsetVideosCountCeil = Number(Math.ceil(e.nativeEvent.contentOffset.y / videoHeight));
    const offsetVideosCount = offsetVideosCountCeil === this.props.videos.length - 1
      ? this.props.videos.length - 1 : offsetVideosCountRound;

    if (this.state.offsetVideosCount !== offsetVideosCount) {
      if (offsetVideosCountCeil === this.props.videos.length) {
        this.scrollResponder._root.refs._rnkasv_keyboardView.scrollToEnd({animated: true});
      } else {
        this.scrollResponder._root.scrollToPosition(
          e.nativeEvent.contentOffset.x,
          offsetVideosCount * videoHeight,
          true
        );
      }

      this.setState({
        offsetVideosCount,
      });
    }
  }

  whatsOnEnd() {
    console.log('fire');
    if(typeof this.videos !== 'undefined' || this.videos.length > 0) {
      this.videos.forEach((video, videoId) => {
        console.log(video, videoId);
      });
    }
  }

  renderListError() {
    console.log(arguments);
  }

  render() {
    return (
      <Container style={stylesVars.container}>
        <Header
          hasTabs
          noShadow
        >
          <Grid>
            <Row style={{ minHeight: this.state.hashtagContainerMinHeight }}>
              <TextInput
                inputStyle={styles.inputStyle}
                underlineColorAndroid="transparent"
              />
            </Row>
          </Grid>
        </Header>
        <Content
          ref={(ref) => this.scrollResponder = ref}
          onScroll={this.whatsOnScroll.bind(this)}
          scrollEventThrottle={200}
        >
          <List
            styles={styleVars.ListViewStyle}
            dataArray={this.state.items}
            renderRow={this.renderRow.bind(this)}
            initialListSize={2}
            renderError={this.renderListError}
            enableEmptySections={true}
            onEndReached={this.whatsOnEnd.bind(this)}
          />
        </Content>
      <Footer style={styles.footerStyle}>
        <FooterTab>
          <Button>
            <Text>Home</Text>
            <Icon name="ios-home"/>
          </Button>
          <Button>
            <Text>NewClip</Text>
            <Icon name="logo-youtube"/>
          </Button>
          <Button>
              <Text>Chat</Text>
              <Icon name="ios-chatbubbles"/>
          </Button>
          <Button>
            <Text>Selection</Text>
            <Icon name="ios-thumbs-up"/>
          </Button>
          <Button>
            <Text>Setting</Text>
            <Icon name="ios-cog"/>
          </Button>
        </FooterTab>
      </Footer>
    );
  }
}

AppRegistry.registerComponent('App');

Screenshot of emulator/device

image

Is the bug present in both ios and android or in any one of them?

In both.

Any other additional info which would help us debug the issue quicker.

Well, it works with regular ListView, so its specific to List component and Content. You can test it first with ListView and do it with List to see the difference.

Most helpful comment

You don't really need Content if you're using List, so please replace it with View and check.

All 9 comments

You don't really need Content if you're using List, so please replace it with View and check.

Changed it to this:

          <List
            ref={(ref) => this.scrollResponder = ref}
            onScroll={this.whatsOnScroll.bind(this)}
            scrollEventThrottle={200}
            styles={styleVars.ListViewStyle}
            dataArray={this.state.dataSource}
            renderRow={this.renderRow.bind(this)}
            initialListSize={2}
            renderError={this.renderListError}
            enableEmptySections={true}
            onEndReached={this.whatsOnEnd.bind(this)}
          />

now its working fine, but I cannot get:

this.scrollResponder._root.refs._rnkasv_keyboardView.scrollToEnd({animated: true});
and

        this.scrollResponder._root.scrollToPosition(
          e.nativeEvent.contentOffset.x,
          offsetVideosCount * videoHeight,
          true
        );`

scrollToPosition on ScrollView reference can be replaced with

scrollToPosition({
          x: e.nativeEvent.contentOffset.x,
          y: offsetVideosCount * videoHeight,
          animated: true
       });`

But because in List there is no ScrollView reference, how to walkaround this issue?

You can take the listView ref as well, to scroll.

I can't did you take a look whats in console.log(ref)?
Just:

<List
            ref={(ref) => console.log(ref)}

and you will see, that there is no scrollView anywhere, not in _root.refs, not in refs, not in any other properties and none of them has scrollTo or scrollToEnd.

Ideally if ListView has scrollTo function, List should too. And AFAIK, ListView is an extension of ScrollView. I'm not very sure though.

Well on List it doesn't work on ListView it does scrollTo, think its because how scrollTo is coded, it uses scrollComponent reference inside ListView and ListView is not referenced to List. So the same problem is here: https://github.com/GeekyAnts/NativeBase/issues/555

So someone just needs to do a small fix and add new release. :-)

What you saying is true, but in List component itself the reference of ListView is not returned to List component. Its only returned if dataArray and renderRow is not used, but then the Views reference is returned. Well at least its like that in latest version, checkout List here: https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/List.js#L21 no ref in JSX on ListView and I think its not passed via ref as it is special property, which can go only one level in hierachy and unless you in child components attached more references to parent components. I can only hardcode that ref in.

As you can see here: https://facebook.github.io/react/docs/refs-and-the-dom.html That ref can't assign refs of child component by going ref. What I mean:

ref={(ref) => this.blabla = ref((ref2) => this.blabla2 = ref2)}

is not possible. If you want to get childrens children reference, you have to assign it to childrens children, else its null, so in other words nesting by default is not available, unless you create it.

I've added this pull request, which should fix the issue: https://github.com/GeekyAnts/NativeBase/pull/561

Using this.refs.myList._root.root.scrollTo({x: 0, x: 0, animated: true}) worked for me. Note the extra root.

@daniel-van-niekerk, yes it does, because it is ListView method, not ScrollView. ScrollToEnd or onEndReached is scrollView methods, so they do not get back references from ListView reference. That why you need reference to _scrollComponent from ListView, so you could access ScrollView methods.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

muthuraman007 picture muthuraman007  路  3Comments

kitsune7 picture kitsune7  路  3Comments

aloifolia picture aloifolia  路  3Comments

natashache picture natashache  路  3Comments

eggybot picture eggybot  路  3Comments