React Native ListView: Wrong row removed when deleting
<p>I know this has been asked before, see for example <a href="https://stackoverflow.com/questions/31424212/listview-removing-wrong-row-on-datasource-update">here</a>, <a href="https://stackoverflow.com/questions/29351259/how-to-add-delete-item-into-listview">here</a> and <a href="https://stackoverflow.com/questions/33060530/react-native-remove-item-from-listview">here</a>.</p>

<p>However, none of the answers/comments are satisfactory. They either tell you to clone the data which doesn't solve the problem or to set a unique key on the ListView which solves the problem but creates a new one.</p>

<p>When you set a unique key on the ListView and update it after deletion the entire view renders again and scrolls to the top. A user who scrolls down a list to delete an item expects the list to maintain it's position.</p>

<p>Here's a contrived example, using what I would assume was the correct way of cloning the data:</p>

<pre class="lang-js prettyprint-override"><code>var ListViewExample = React.createClass({

  getInitialState() {
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) =&gt; {
      r1 !== r2
    }});
    var rows = ['row 1', 'row 2', 'row 3'];
    return {
      dataSource: ds.cloneWithRows(rows),
    };
  },

  _deleteRow() {
    var rows = ['row 1', 'row 3'];
    this.setState({dataSource: this.state.dataSource.cloneWithRows(rows)})
  },

  renderRow(rowData, sectionID, rowID) {
    return &lt;TouchableOpacity onPress={this._deleteRow}
      style={{height: 60, flex: 1, borderBottomWidth: 1}}&gt;
      &lt;Text&gt;{rowData}&lt;/Text&gt;
    &lt;/TouchableOpacity&gt;
  },

  render() {
    return (
      &lt;ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
      /&gt;
    );
  }

});
</code></pre>

<p>And <a href="https://i.stack.imgur.com/TkxUw.gif" rel="nofollow noreferrer">here</a> you can see a gif of it in action where the wrong row get's removed. The alternative, to set the <code>key</code> prop of the ListView does remove the correct row but as I said will cause the list to scroll to the top.</p>

<p><strong>UPDATE</strong></p>

<p>I've already accepted Nader's answer below but this does not seem to be the right way because it does not call <code>rowHasChanged</code> on the dataSource. It also doesn't conform to the ListView documentation. It did solve my problem so I'll leave it marked as the answer but I have the feeling this is a workaround rather than the correct solution</p>

<p><strong>ANOTHER UPDATE</strong></p>

<p>Okay this is awkward but my <code>rowHasChanged</code> function was missing the <code>return</code> statement. Too much ES6 sugar in my coffee I guess.</p>

<p>So in conclusion, if you screw up your <code>rowHasChanged</code> function, <code>this._ds</code> will work for some reason. If you do things the right way to begin with you should probably use something like:</p>

<pre><code>this.setState({
  dataSource: this.state.dataSource.cloneWithRows(rows)
});
</code></pre>

<p>when updating your <code>dataSource</code>.</p>

<p>Remember also that you need a new <code>rows</code> datablob. Mutating it in place with something like <code>cloneWithRows(rows.push(newRow)</code> will not work whereas <code>cloneWithRows(rows.concat([newRow])</code> will.</p>