11

I'm not sure why the ListView isn't updating when my data changes. I've striped my code down to make it easy to read and created a rnplay:

https://rnplay.org/apps/ivG0mg

What I expect to happen is, the user clicks the list item and the row's bool isCollapsed is toggled making the background red. What actually happens is the datasource is updated but the list view doesn't recognize the change and nothing new is rendered. Any ideas?

'use strict';

var React = require('react-native');

var {
    ScrollView,
    Text,
    Image,
    StyleSheet,
    TouchableHighlight,
    AppRegistry,
    View,
    ListView,
} = React;

var styles = StyleSheet.create({
    container: {
        flex: 1,
        flexDirection: 'column',
        padding: 15,
    },
    red: {
        backgroundColor: "red",
    }
});

var foods = [
    {key: 'Almond Milk (Homemade)', details:''},
    {key: 'Club Soda', details:'', isCollapsed: true},
    {key: 'Coconut Milk/Cream', details:''},
    {key: 'Coconut Water', details:''},
    {key: 'Coffee/Espresso', details:'', isCollapsed: true},
    {key: 'Fruit Juice', details:''},
    {key: 'Kombucha', details:''},
    {key: 'Mineral Water', details:''},
    {key: 'Unsweetened Tea', details:''},
    {key: 'Water', details:''},
    {key: 'Fruit Juice', details:''},
    {key: 'Kombucha', details:''},
    {key: 'Mineral Water', details:''},
    {key: 'Unsweetened Tea', details:''},
    {key: 'Water', details:''},
    {key: 'Fruit Juice', details:''},
    {key: 'Kombucha', details:''},
    {key: 'Mineral Water', details:''},
    {key: 'Unsweetened Tea', details:''},
    {key: 'Water', details:''},
    {key: 'Fruit Juice', details:''},
    {key: 'Kombucha', details:''},
    {key: 'Mineral Water', details:''},
    {key: 'Unsweetened Tea', details:''},
    {key: 'Water', details:''},
    {key: 'Fruit Juice', details:''},
    {key: 'Kombucha', details:''},
    {key: 'Mineral Water', details:''},
    {key: 'Unsweetened Tea', details:''},
    {key: 'Water', details:''},
];

class SampleApp extends React.Component{
    constructor(props){
        super(props);
        var ds = new ListView.DataSource({
            rowHasChanged: (row1, row2) => row1 !== row2,
        });
        this.state = {
            dataSource: ds.cloneWithRows(foods),
        };
    }
    _renderRow(data, sectionID, rowID) {
                return (
                        <TouchableHighlight 
                            style={[
                                    styles.container,
                                    data.isCollapsed && styles.red]}
                onPress={()=>this.onCollapse(rowID)}>
                <Text>{data.key}</Text>
            </TouchableHighlight>
        );
    }
    onCollapse(rowID: number) {
        console.log("rowID", rowID);
        foods[rowID].isCollapsed = !foods[rowID].isCollapsed;
        this.setState({dataSource: this.state.dataSource.cloneWithRows(foods)});
    }
    render() {
        return(
            <ListView
                style={styles.subContainer}
                dataSource={this.state.dataSource}
                renderRow={this._renderRow.bind(this)}
                initialListSize={15}/>
        )
    }
};

AppRegistry.registerComponent('SampleApp', () => SampleApp);
Dev01
  • 13,292
  • 19
  • 70
  • 124

2 Answers2

11

Here's a link to a working version:

https://rnplay.org/apps/GWoFWg

These are the changes I need to make to fix it:

constructor(props){
    super(props);
    var ds = new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
        dataSource: ds.cloneWithRows(foods),
        db: foods,
    };
}

and this:

onCollapse(rowID: number) {
    var newArray = this.state.db.slice();
    newArray[rowID] = {
        key: newArray[rowID].key,
        details: newArray[rowID].details,
        isCollapsed: newArray[rowID].isCollapsed == false ? true : false,
    };
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(newArray),
        db: newArray,
    });
}
Dev01
  • 13,292
  • 19
  • 70
  • 124
2

Looked like this would be the solution you need React-Native Updating List View DataSource

But I played around with it a little and also couldn't get it working. https://rnplay.org/apps/sk_ukQ

Not sure if it will help, but I tried this setting the array as blank and am able to clear the entire list... this.setState({dataSource: this.state.dataSource.cloneWithRows([])});

I think for some reason it's using a cached version of the original array rather than the updated array. Just not sure why.

Community
  • 1
  • 1
Chris Geirman
  • 9,474
  • 5
  • 37
  • 70
  • Thanks for the link. The answer was in the comments. I had to rebuild the item when making a change to it. Seems like a bug that I hope gets fixed at some point. https://rnplay.org/apps/GWoFWg – Dev01 Oct 30 '15 at 05:02
  • 1
    Ah, good! Glad I was at least able to point you in the right direction. Thanks for sharing the solution. – Chris Geirman Oct 30 '15 at 15:12
  • 1
    Also, you might be able to help this guy out... http://stackoverflow.com/questions/33436902/how-force-redraw-listview-when-this-state-changed-but-not-the-datasource – Chris Geirman Oct 30 '15 at 15:15
  • Just answered that question too. You're like the Robin Hood of stackoverflow. Getting questions answered right and left. :) – Dev01 Oct 30 '15 at 15:37
  • Ha, just connecting dots :) – Chris Geirman Oct 31 '15 at 02:55
  • I just came across this. claims to be an updatable listview component for both iOS and Android with pull-to-refresh. Looks sweet. Thought it might be helpful. https://github.com/FaridSafi/react-native-gifted-listview – Chris Geirman Oct 31 '15 at 22:59
  • Hey @TomKrones, I'm still learning things about React and haven't worked with the lifecycle methods much yet, but I'm now thinking that componentWillReceiveProps() may be the real solution needed here. I'll try to find time to play with and prove this out, but thought I'd pass along the info in case you had time (and the desire) sooner. https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops – Chris Geirman Nov 03 '15 at 16:03
  • Oooh, replaceState() and forceUpdate() may have potential too! https://facebook.github.io/react/docs/component-api.html#replacestate – Chris Geirman Nov 03 '15 at 16:40
  • Yeah, the current solution is definitely a workaround. I feel like it's a bug but maybe there is a more proper way to do it. – Dev01 Nov 03 '15 at 20:25