question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[FlatList] FlatList unmounts its header component continuously

See original GitHub issue

Description

I have a FlatList with ListHeaderComponent, that is composed with some pickers, when you scroll down or up the header component is being mounted and unmounted continuously, causing the header status to be reset, so pickers will lose their current value.

Reproduction Steps and Sample Code

My implementation:

constructor(props) {
        super(props);
        this.state = {
            refreshing: false,
            data: []
        };
    }

getItemsData(jobList, start, count) {
        var data = [];
        for (var i = start; i < count + start && i < jobList.length; i++)
        {
            data.push(jobList[i]);
        }
        return data;
    }

onEndReached() {
        this.setState({
            data: this.state.data.concat(this.getItemsData(this.props.jobs.jobList, this.state.data.length, 5))
        });

    }

shouldItemUpdate(props, nextProps) {
        if (_.isEqual(props, nextProps))
        {
            return false;
        }
        else
        {
            return true;
        }
    }

componentWillReceiveProps(nextProps) {
        if (nextProps.jobs.jobList.length > 0 && !nextProps.jobs.isFetchingJobList)
        {
            this.setState({
                data: this.getItemsData(nextProps.jobs.jobList, 0, 10)
            });
        }
    }

render() {
        return (
            <View>
                <FlatList
                    ListHeaderComponent={() => <JobListHeader onApplyFilters={this.onApplyFilters.bind(this)}/> }
                    data={this.state.data}
                    renderItem={this.renderItem.bind(this)}
                    keyExtractor={this.keyExtractor}
                    numColumns={1}
                    onEndReached={this.onEndReached.bind(this)}
                    initialNumToRender={5}
                    maxToRenderPerBatch={4}
                    shouldItemUpdate={this.shouldItemUpdate.bind(this)}
                    onEndReachedThreshold={6}
                    refreshing={this.state.refreshing}
                    onRefresh={this.onRefresh.bind(this)}
                    ref="listRef"
                />
            </View>
        );
    }

JobListHeader render only <View></View>

It seems that onEndReached triggers unmount and mount.

Additional Information

  • React Native version: 0.43.2
  • Platform: Android
  • Development Operating System: Linux
  • Dev tools: Android Studio 2.3, Android SDK 23

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:7
  • Comments:14 (2 by maintainers)

github_iconTop GitHub Comments

42reactions
sahrenscommented, May 10, 2017

This is a little nuanced…

tl;dr: always pre-bind functions you pass as props so you don’t re-create the function on every render.

So ListHeaderComponent is of type ReactClass, which means that when you pass in a function like you’re doing, it’s not just called as a plain function (like renderItem), but as a stateless, functional react component (hence the name…*Component). This means that if you re-create and re-bind the function in render like you are doing (e.g. any time you call setState in onEndReached), React will treat it as a new react component class and will destroy and recreate the instance every time, just as if you were switching back and forth between <Text> and <View>.

I would recommend turning that function into an autobound method like so:

_headerComponent = () => <JobListHeader onApplyFilters={this.onApplyFilters.bind(this)} />;

then when you set the prop, it will always be the same thing and react will update the instance rather than re-creating from scratch:

ListHeaderComponent={this._headerComponent}

If the auto-binding initializer syntax doesn’t work for you for some reason, you can bind it in the constructor instead.

I also recommend auto-binding for all your other props as well, like renderItem, because it will skip unnecessary work and wasted memory with constantly re-binding the same functions, and will skip unncessary re-renders when doing the PureComponent shallow equality checks.

Couple other notes:

  1. shouldItemUpdate was deprecated so it no longer does anything.
  2. Don’t use string refs, use functional refs.
6reactions
sahrenscommented, May 10, 2017
constructor(props, context) {
  super(props, context);
  this._headerComponent = this._headerComponent.bind(this);
  ...
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

How to prevent flatlist header or footer from re-render in ...
This way they will always re-rendering if component will be executed. For performance reasons you better define your function outside and call ...
Read more >
Optimizing Flatlist Configuration - React Native
The more complex your components are, the slower they will render. Try to avoid a lot of logic and nesting in your list...
Read more >
How to use the FlatList Component — React Native Basics
What is the FlatList component? It's an easy way to make an efficient scrolling list of data. Not only is it efficient but...
Read more >
Optimizing React performance by preventing unnecessary re ...
Re-rendering React components unnecessarily can slow down your app and make the UI feel unresponsive. This article explains how to update ...
Read more >
How To Prevent A Header Of Flatlist From Going Down When ...
. that I replace the pager buttons with a continuously refreshing or infinite scrolling list. I assigned it a header component that renders...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found