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

Created on 15 Feb 2016  路  19Comments  路  Source: facebook/react-native

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)

Locked

Most helpful comment

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.

All 19 comments

Hey npomfret, thanks for reporting this issue!

React Native, as you've probably heard, is getting really popular and truth is we're getting a bit overwhelmed by the activity surrounding it. There are just too many issues for us to manage properly.

  • If you don't know how to do something or something is not working as you expect but not sure it's a bug, please ask on StackOverflow with the tag react-native or for more real time interactions, ask on Discord in the #react-native channel.
  • If this is a feature request or a bug that you would like to be fixed, please report it on Product Pains. It has a ranking feature that lets us focus on the most important issues the community is experiencing.
  • We welcome clear issues and PRs that are ready for in-depth discussion. Please provide screenshots where appropriate and always mention the version of React Native you're using. Thank you for your contributions!

I have the same error, did you manage to fix this ?
I have searched in stackoverflow and I found this question with the same error, and he said that it only occurred when there is a big number of items in the datasource, I tried to minimize the number of elements to just two and it works without throwing this error, maybe this is a bug ?

See my sample code, you don't need many data items to make it go wrong, but there do need to be enough that they cannot all be rendered and the component needs to scroll.

I've mentioned it on productpains, please up vote if you can.

I've found a work around for now which is to use the _renderScrollComponent_ property. I found a third party implementation called InvertibleScrollView which doesn't seem to suffer from the same problem:

renderScrollComponent={props => <InvertibleScrollView inverted />}

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)});

Thanks @kenny1har that worked for me too

Okay, it is proper way @kenny1har mentioned, but we would not have this checkpoint.

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 />}.

hi.I meet the same question.I have solved it.
when change datasource,you should render a blank screen first,then render the screen you want to show. I think its a bug.

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.

@apalmblad thanks! fixed it for me :)
(used scrollRenderAheadDistance in my case)

Using
renderScrollComponent={(props) => <RecyclerViewBackedScrollView {...props}/>}

On a static (never changing) list results in nothing being displayed at all in android!

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 :+1:

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

Is there any pull request that solves this problem?

I don't think so, but the work around is to use a renderScrollComponent. I think probably the docs just need updating.

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

Not sure when this got fixed but it seems fine in rn 0.26

Currently stuck with 0.24 but I'll keep this in mind. Thanks for checking

So I'm looking at Crashlytics for our app, and noticing a ton of different variations of this error where all stack traces end up at textView.checkForRelayout.

  • Does this only happen with ListViews?
  • The underlying problem is that the layout params are null in checkForRelayout. How does the view set up ever get into that state?

As far as I'm aware the original issue i reported is fixed.

Was this page helpful?
0 / 5 - 0 ratings