i6mi6 / react-native-parallax-scroll-view

A ScrollView-like component with parallax and sticky header support.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

onEndReached constantly called in nested FlatList

samiede opened this issue · comments

Hey guys,

I've written a component which takes content as a prop, which is then rendered within the ParallaxScrollView, which basically looks like this:

<ParallaxScrollView
    renderForeground={...}
    renderBackground={...}
    etc
>
{this.props.renderContent()}
</ParallaxScrollView> 

I use this in several parts of the app and it works like a charm, but on one screen, this content is a FlatList, that makes use of onEndReached to load new content as the user scrolls down.
Using the FlatList inside the ParallaxScrollView constantly calls onEndReached, which is not the behavior I want. Is there any way to circumvent this?

Thanks!

Is there any solution, You figured it out?

Is there any solution, You figured it out?

I wrote my own Component that uses a Section List (because for some reason it performs better than a FlatList). I added a SectionList Header, which I animate using the scroll position of the section list with the native driver (also important for performance, but it restrains you and the transforms you can make!)
I'm planning to release the module as library if I find the time to make it generally applicable, but I think for now that is the easiest solution!

Isn't somehow we can apply fix in this library, did you find the cause for onEndReached repeatedly calling?

The problem is that react can't handle onEndReached with nested ScrollViews because it doesn't know which scroll view has reached the end. Since you're embedding the FlatList into a ScrollView, and the FlatList basically is the last element within the ScrollView, onEndReached is constantly triggered for the ScrollView, but is also passed on to the FlatList. You have to remove the nesting for react to be able to determine the distance to the end.

I've handled it approximately like this:

render() {

    const HEADER_MIN_HEIGHT = 50;
    const HEADER_MAX_HEIGHT = this.props.maxHeaderHeight;
    const HEADER_SCROLL_DISTANCE = HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT;

    const headerTranslate = this.state.scrollY.interpolate({
      inputRange: [0, HEADER_SCROLL_DISTANCE],
      outputRange: [0, -HEADER_SCROLL_DISTANCE],
      extrapolate: 'clamp'
    })

    const backgroundScroll = this.state.scrollY.interpolate({
      inputRange: [0, HEADER_SCROLL_DISTANCE],
      outputRange: [0, HEADER_SCROLL_DISTANCE / 1.25],
      extrapolate: 'clamp'
    })

    const imageOpacity = this.state.scrollY.interpolate({
      inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE],
      outputRange: [1, 1, 0],
      extrapolate: 'clamp',
    });

    const fixedHeaderOpacity = this.state.scrollY.interpolate({
      inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE],
      outputRange: [0, 0, 1],
      extrapolate: 'clamp',
    });
    
    {*/ If you want stuff to stay at a certain position in the Header for some cool layer effects, use this translation */}
    const reverseHeaderTranslate = this.state.scrollY.interpolate({
      inputRange: [0, HEADER_SCROLL_DISTANCE],
      outputRange: [0, HEADER_SCROLL_DISTANCE],
      extrapolate: 'clamp'
    })



    return (
      <View style={styles.fill}>
        {/* Animated Header. This stuff is going to appear in within the section header because it's above the List, you'll animate this with the scroll position */}
        <Animated.View style={[styles.header, 
                             {height:HEADER_MAX_HEIGHT, transform: [{translateY: headerTranslate}]}]}>
          {/* If you want the background to scroll with a different speed, give it another translation */}
          <Animated.Image
            style={[{ height: HEADER_MAX_HEIGHT, transform: [{translateY: backgroundScroll}]}]}
            resizeMode='cover'
            source={this.props.imageSource}
          />
          {/* Stuff  you want in the animated header*/}
        </Animated.View>
        {/*List*/}
        <AnimatedFlatList
          data={this.props.data}
          sections={[{title:'', data: this.props.data}]}
          ListFooterComponent={this.props.ListFooterComponent}
          renderItem={this.props.renderItem}
          keyExtractor={this.props.keyExtractor}
          onEndReachedThreshold={this.props.onEndReachedThreshold}
          onEndReached={this.props.onEndReached}
          style={this.props.listStyle}
          onScroll={Animated.event(
            [{nativeEvent: {contentOffset: {y: this.state.scrollY}}}],
            {useNativeDriver: true}
          )}
          renderSectionHeader={({ section }) => (
            <View style={{ height: HEADER_MAX_HEIGHT }} />
          )}
        >
        </AnimatedFlatList>
        {/* Fixed Header. This stuff is appearing OVER the stuff above, set the position to 'absolute' */}
        <Animated.View
          style={[
            styles.fixedHeader,
            { opacity: fixedHeaderOpacity}
          ]}>
        </Animated.View>
        {/*content for the header*/}
      </View>
    );
  }
}
`