Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react-native: ListView, how to push new rows from top

I have been trying to understand how I could manage to make a ListView that can recieve often new data and push it to the front. With the code below, the list does not reflect change correctly, instead of having a new row at the top, the last one is beeing duplicated..

'use strict';

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ListView,
  TouchableHighlight,
} = React;

var listview = React.createClass({
  rows: [],

  _rowHasChanged: function (r1, r2) {
    // if (r1 !== r2)
    //   console.log('rowChanged', r1, r2);
    // else
    //   console.log('row didn\'t Changed', r1, r2);
    return r1 !== r2;
  },

  getInitialState: function() {
    return {
      dataSource: new ListView.DataSource({rowHasChanged: this._rowHasChanged}),
    };
  },

  componentDidMount: function () {
    this.rows = [
      {id: 0, text: '0: text'},
      {id: 1, text: '1: text'},
      {id: 2, text: '2: text'},
      {id: 3, text: '3: text'},
    ];

    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(this.rows),
    });
  },

  pushTopRow: function () {
    var id = this.rows.length;
    this.rows.unshift({id: id, text: id + ': text'});

    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(this.rows),
    });
  },

  render: function() {
    return (
      <View style={styles.container}>
        <ListView
          dataSource={this.state.dataSource}
          renderRow={(rowData) => <Text key={rowData.id}>{rowData.text}</Text>}
        />
        <TouchableHighlight
          onPress={this.pushTopRow}>
          <Text>PUSH TOP</Text>
        </TouchableHighlight>
      </View>
    );
  },
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'orange',
  },
});

AppRegistry.registerComponent('listview', () => listview);

then I tried:

pushTopRow: function () {
    var id = this.rows.length;
    var newRow = [{id: id, text: id + ': text'}];
    this.rows = newRow.concat(this.rows);

    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(this.rows),
    });
  },

this actually works, but I don't understand why first method doesn't work. I'm trying to figure out what is the best practises for handling with ListView:

  • Am I supposed to use cloneWithRows with a new array in any case, even for a single add/delete operation ?
  • Is there anything else to setup for the ListView/DataSource mechanics to works correctly with minimum redraw (keys, hasRowChanged specific comparision ?),

kind of lacking of documentations and exemples, it would be great to clarify it for me and anyone. thanks you & have a good code day.

like image 395
Akabab Avatar asked Aug 02 '15 17:08

Akabab


1 Answers

ListView assigns component keys by data source index, and consequently will rerender all rows whenever the datasource is prepended to. If your row data has any unique id, you can create a modified ListView that uses that as the key. See for example PrependableListView which will use the id attribute on your row data.

Note: this component is not generalized and will fail if there is no id property defined.

Note: I realize this does not answer your whole question, just a specific peculularity about prepending to the datasource.

like image 126
zackz Avatar answered Oct 26 '22 00:10

zackz