react-native: [0.19][ListView][Android] ListView crashes when ListView.DataSource is updated (with example code)

Updating a ListView.DataSource on Android causes a NullPointerException if list is long enough that it does not all fit on screen.

Here’s a sample project with code that illustrates the bug: https://github.com/npomfret/react-native-bug-reports/tree/master/ListViewUpdatingBug

It does not appear to happen on IOS.

screen shot 2016-02-15 at 13 39 45

Exception in native call from JS java.lang.NullPointerException: Attempt to read from field 'int android.view.ViewGroup$LayoutParams.width' on a null object reference at android.widget.TextView.checkForRelayout(TextView.java:7128) at android.widget.TextView.setText(TextView.java:4342)

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 19 (7 by maintainers)

Most upvoted comments

I updated the reproduction from @npomfret to 0.21 and the issue still happens.

Through testing, I found a few workarounds:

  • Adding a the property renderScrollComponent to the listview, ie renderScrollComponent={(props) => <React.RecyclerViewBackedScrollView {...props}/>}
  • Setting scrollRenderAheadDistance to 0 on the list view seems to fix the issue, but crashes with values > 0
  • Setting removeClippedSubviews to false also fixes the issue
  • Finally, leaving ListView as-is, but setting style={{overflow:hidden}} on the component returned by renderRow also fixes the issue.

Now, from the docs:

A performance optimization for improving scroll perf of large lists, used in conjunction with overflow: ‘hidden’ on the row containers. This is enabled by default.

So it looks like the solution is already documented; however I’d argue that making the results of renderRow require a certain style setting to work by default is not the friendliest option. I’d say that applying that style to the result of renderRow would be a reasonable solution, but until that change is made setting the default back to false for removeClippedSubviews would work.

I have the same problem with my ListView Footer. Never encountered something like that before. I’m on 0.22.0. If I have some variable in a textfield, it is not working anymore.

renderFooter:

<Text>{var}</Text> // not working
<Text>xyz</Text> // working

thanks to @apalmblad for the workaround with removeClippedSubviews or overflow: hidden, both are working for me 👍

edit: what is better for the performance, setting overflow or removeClippedSubviews?

Set the data source to empty array before changing. It works. var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); this.setState({dataSource: ds.cloneWithRows([])}); this.setState({dataSource: ds.cloneWithRows(data)});

I tried that and it broke my pull to refresh. I had to do removeClippedSubviews={false} to fix the issue

Setting the datasource to an empty array may work but I believe it will cause flicker because the UI will clear itself out before redrawing your non-empty data set.

I’ve found that setting the renderScrollComponent to anything other than the default is a satisfactory workaround. In my case I’ve used:

renderScrollComponent={props => <RecyclerViewBackedScrollView />}.