Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to improve FlatList render performance for large list with ReactNative 0.43?

I am trying to render a list of ~250 images in 3 columns using FlatList in RN0.43, and I change the width of the images in the onLayout function of the FlatList to fit the width of screen.

The initial performance is ok, but after some scrolling up/down, sometimes it takes a second or 2 until images are shown.

it is even worse if I change to screen orientation, it takes 2~3 seconds to get screen updated.

a few findings:

  1. after screen rotation, it takes a second or 2 until FlatList.onLayout is called

  2. after FlatList.onLayout and update of image width, each image (about half of the list, ~150 images; while only ~15 are shown) is rendered 2~4 times, while render() is only called once.

question:

  1. how can I modify the code to improve the performance?
  2. in the getItemLayout() of a multicolumn list, should the offset be something like (itemHeight + separatorHeight) * (index%numColumns)?

Thanks.

tested on: GalaxySII (4.1.2) and Android SDK emulator (7.1.1)

var data = [
    require('./res/img/Search.png'),
    require('./res/img/test - Copy.png'),
    // ~250 items
    ...];

class app extends React.Component {
    renderItem (info, width) {
        console.log('renderItem', info.index);
        if(width !== this.width) {
            this.imageStyle = {width: width-MarginHorizontal , height: width-MarginHorizontal, resizeMode: 'contain'};
        }
        return (
            <Image
                source = {info.item}
                key = {info.index}
                style={this.imageStyle}/>
            );
    }

    render() {
        console.log('Test.render');
        return (
            <View style={{
                flex: 1,
                flexDirection: 'row',
                justifyContent: 'flex-start',
                alignItems: 'center',
                backgroundColor: '#F5FCFF'
            }}>
                <GridList
                    numColumns={3}
                    columnWrapperStyle={{ alignItems: 'center', marginVertical: 5, justifyContent: 'space-around'}}
                    data={data}
                    renderItem={this.renderItem}
                />
            </View>
        );
    }
}

class GridList extends Component {
    onLayout(event) {
        console.log('GridList.onLayout()');
        let newColumnWidth = event.nativeEvent.layout.width/ this.numColumns;
        this.layout = Object.assign({},event.nativeEvent.layout);
        if( undefined === this.columnWidth  || Math.abs(newColumnWidth - this.columnWidth) > WidthTolerance ) {
            this.columnWidth = newColumnWidth;
            if(this.isComponentMounted) {
                this.setState({renderCount: this.state.renderCount+1});
            } else {
                this.state.renderCount +=1;
            }
        }
    }
    render() {
        console.log('GridList.render()');
        return (
            <FlatList
                {...(this.modifiedProps)}
                renderItem={(info) => { return this.props.renderItem(info, this.columnWidth); }}>
                {this.props.children}
            </FlatList>
        );
    }
}
like image 341
John Ng Avatar asked Apr 13 '17 03:04

John Ng


People also ask

How do you optimize a large list of items on FlatList?

Use basic components​ The more complex your components are, the slower they will render. Try to avoid a lot of logic and nesting in your list items. If you are reusing this list item component a lot in your app, create a component only for your big lists and make them with as little logic and nesting as possible.

When should I use Scrolllist instead of FlatList?

ScrollView renders all its react child components at once, but this has a performance downside. FlatList renders items lazily, when they are about to appear, and removes items that scroll way off-screen to save memory and processing time.


Video Answer


1 Answers

Disclaimer: I know that the question is old, but here is my response anyways.

My app has a hand full of lists with 500+ items. So, we got to a point where the app was crashing on popular not-bad phones. Then I've made this extensive research about performance on FlatLists.

The FlatList component was presented as a alternative for the old ScrollView. The problem is that ScrollViews render all your list at once so they perform visually better, but there is a trade off in memory consumption, that leads to app crashes.

So Flat Lists are a necessary evil. They essentially only render items that are visible, which is a huge gain on memory consumption, but a pain for visual performance, specially for heavy/complex items, that happens to be your case with those responsive images.

How to workaround?

There are a lot of strategies that you can implement to mitigate your problem.

  • Use cached and performatic images, such as react-native-fast-image. Every operation that you can remove or abbreviate for freeing the Javascript thread: do it (every image is a new Image(), so, if they are cached, you have your loaded hook called sooner)

  • Your list item component is a read-only component, which is supposed to be 'dumb'. Implement a shouldComponentUpdate() { return false } or a more solid update control method as needed. This is HUGE perf boost.

  • Remove console.logs anywhere near your list. They slow the Javascript thread really bad.

  • Build your app for production and test it. It becomes almost always twice or three times faster. Dev env is slow because of debugging.

  • Give this article a good read for more strategies.

Conclusion

FlatList IS a slow component. This is a known, open and well documented issue. Do what you can to improve it, and let's hope future releases may fix this.

like image 130
Filipe Merker Avatar answered Oct 18 '22 22:10

Filipe Merker