react-native: FlatList - while list is scrolling/rendering items the JS thread is blocked, onPress are super delayed.

Description

While FlatList is scrolling and/or rendering items the JS thread is blocked… any events (onPress etc) on TocuchableWhatever are ingored or processed after FlatList finishes rendering. How can I pause FlatList rendering and process onPress?

Content rendered by FlatList while scrolling uses InteractionManager so if you want to pause it you just need to create an interaction handle. © @sahrens

I’ve tried that on my ListItem:

export default class ListItem extends Component { 

    shouldComponentUpdate() {
        return false;
    }

    render() {
        const {item} = this.props;

        return (
                <TouchableHighlight style={styles.item} underlayColor='rgba(255,255,255,.3)' activeOpacity={0.7} onPress={this._onPress}>
                     <View style={styles.row}>
                        <Image style={styles.image} source={{ uri: item.imgUrl}} />
                        <View style={styles.details}>
                            <BaseText style={styles.caption} weight={600}>{item.caption}</BaseText>
                            <BaseText style={styles.points}>{item.points} points</BaseText>
                        </View>
                        <TouchableOpacity hitSlop={{top: 10, left: 10, right: 10, bottom: 10}} >
                            <Icon style={styles.icon} name={item.favorite ? 'heart' : 'heart-outline'} size={24} color={'white'} />
                        </TouchableOpacity>
                    </View>
                </TouchableHighlight>
                )
    }

    _onPress = () => {
        const handle = InteractionManager.createInteractionHandle();
        this.props.onPress(this.props.item.key);
        InteractionManager.clearInteractionHandle(handle);
    }

}

but it does not fix the issue. Am I doing it wrong?

Additional Information

  • React Native version: 0.42.0
  • Platform: tired only on iOS (device)
  • Operating System: MacOS
  • Dev tools: Xcode

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 53
  • Comments: 63 (10 by maintainers)

Commits related to this issue

Most upvoted comments

as an alternative and cleaner solution, I ended up wrap FlatList over ScrollView.

something like this:

<ScrollView>
  <FlatList />
</ScrollView>

it worked for me.

Yeah, it is is pretty tricky balancing memory, fill rate, responsiveness, and frame rate but we’ll keep optimizing!

Can you describe more precisely the problem you’re seeing? When FlatList is scrolled (some items are rendered, but still renders offscreen items) and I press on any already rendered item the press is ignored/or is delayed few sec/or TouchableHighlight laggs: underlayColor is visible for few seconds. When I scroll and wait few sec (till it finishes rendering items batch) - everything ok. How much of a delay is there when you press the button? No on press event is fired at all and I have to press several times before ListItem becomes responsive. Sometimes TouchableHighlight laggs: underlayColor is visible for few seconds. In the perf monitor I see that JS FPS is <10 - list is rendering offscreen items(?). If you let it sit for a bit before pressing, is there any delay? Almost no delay. Everything works as expected. Or only if you just finished scrolling? “just finished scrolling” - as described above - there is a noticeable delay. What is triggered by the onpress? Nothing, empty fn.

ListView - no issue.

import React, { PureComponent } from 'react'
import { View, StyleSheet, InteractionManager } from 'react-native'
import { colors,  componentSpecific } from 'src/theme'
import FlatList from 'react-native/Libraries/Experimental/FlatList';
import ListItem from './ListItem'

const { slider, list } = componentSpecific;

const viewabilityConfig = {
  minimumViewTime: 3000,
  viewAreaCoveragePercentThreshold: 100,
  waitForInteraction: true,
};

export default class List extends PureComponent {

    render() {
        return (
            <FlatList
                SeparatorComponent={this._renderSeparatorComponent}
                data={this.props.data}
                getItemLayout={this._getItemLayout}
                key={'List'}
                legacyImplementation={false}
                numColumns={1}
                //onRefresh={this._onRefresh}
                ref={(ref) => { this._listRef = ref; }}
                refreshing={false}
                renderItem={this._renderItemComponent}
                shouldItemUpdate={this._shouldItemUpdate}
                viewabilityConfig={viewabilityConfig}
                keyExtractor={this._keyExtractor}

                // ScrollView props
                directionalLockEnabled={true}

                style={styles.list}
            />
        )
    }

    _renderItemComponent = ({item}) => {
        return (
            <ListItem item={item} onPress={this._pressItem}/>
        )
    }

    _renderSeparatorComponent = () => {
        return (
            <View style={styles.separator} />
        )
    }

    _shouldItemUpdate = (prev, next) => {
        return prev.item !== next.item;
    }

    _getItemLayout = (data, index) => {
        return { length: list.itemHeight, offset: (list.itemHeight + list.separatorHeight) * index, index }
    }

    _keyExtractor = (item) => {
        return item.id.toString();
    }

    _pressItem = (water) => {
   
    }

}
const styles = StyleSheet.create({
    list: {
        flex: 1,
    },
    separator: {
        height: list.separatorHeight,
        backgroundColor: 'white',
        opacity: 0.3,
    },
});

for me, around 70 items, all with same height, had to change TouchableOpacity for TouchableWithoutFeedback, was getting 4-5 second delay for the press to happen (on Android)

Thanks for posting this! It looks like you may not be using the latest version of React Native, v0.53.0, released on January 2018. Can you make sure this issue can still be reproduced in the latest version?

I am going to close this, but please feel free to open a new issue if you are able to confirm that this is still a problem in v0.53.0 or newer.

How to ContributeWhat to Expect from Maintainers

Getting the same issue. ~100 records, about a 1.5s delay. I can tap around 7 times during that time while the list is being rendered before I can navigate to the next stack.

EDIT: Nevermind, its definitely something with my settings. Narrowing down now…

EDIT 2: Its my search bar that I’m passing into ListHeaderComponent. Changing that to a PureComponent didn’t make any difference. Strange.

EDIT 3: Yeah, as soon as my ListHeaderComponent has a height to it (and renders), I get a significant delay.

Performance breaks down around ~50 records or so.

@sahrens so I ended up setting maxToRenderPerBatch={1} it gives me worse fill rate, but a lot better perfomance…

Thanks for your help.

Can’t wait when the cell reuse will be implemented natively… it will be a huge step forward in performance and UX.

It is still a pain in the ass to make a simple FlatList with 100+ Selectable Image. This is a very common pattern, i’m surprised it is that hard to implement 🙄

I have the same problem, it takes few seconds till it onPress() executes after a scroll…

Any advance?

So I also had this problem for long time but then I rearchitectured my listItem template to be PureComponent and the problem disappeared. Hope it helps some other newbie like me 😉

This problem still exists in 0.57.1

I end up using recyclerlistview. It’s working great.

@TGPSKI many thanks for your patch. It improvements user interaction on scenes having lists significantly.

@wellyshen I would advice against using @bolan9999 package, it gave me many null pointer exceptions when using data refresh functionality. Simply not quality or production-ready code.

@sahrens May I know the process of the optimizing of this issue. I use FlatList to render 40 - 50 items the initial rendering performance is great, but the down-side is the action responsive got blocking while items are being scrolled. Which will cause very bad UX for user. But if I switch back to ScrollView the initial rendering performance also cause bad UX for user. So, as a product creator and developer. these trading off really bothering me 😦

My problem is worst than yours. On android touch event(onPress) never work. On android console the log I’m always getting: “E/unknown:ReactNative: Got DOWN touch before receiving UP or CANCEL from last gesture”

navigator: Wix react-native-navigation

Flatlist -> ListItem(onPress)

Having same issue as @gregblass; the simplest ListHeaderComponent (<View style={ {backgroundColor: 'lime', height: 50} }/>) causes a 4s delay. My list contains 100 items.

Delay is gone once I remove the header.

May you provide an example on how to grab an interaction handle in onResponderGrant or shouldSetResponder?

i also had this problem and i fixed the delay on TouchableOpacity inside rows with flex: 1 on FlatList. I also have only stateless components and PureComponents in row depth. The fix with flex 1 is strange but i got inspired by react-native-largelist docs.

hope it helps

<FlatList
        style={{ flex: 1 }}
...

@lsiden I changed it from ListHeaderComponent={this.profileScreenHeader} to ListHeaderComponent={this.profileScreenHeader()} and that prevented it rerendering as it scrolled.

No the problem still exist.

The same problem here. I don’t understand why this issue is closed if the problem is still active? Is there any solution for this?

I spent hours working around this issue i end up switching to DEV=FALSE and the issue disappeared

Anyone have the solution for this? I also have the same problem as well.

FlatList and SectionList is bad performance. Try this,please. may be it is a surprise for you

https://github.com/bolan9999/react-native-largelist