2016-02-15 50 views
9

我知道这已经被问过,例如参见here,herehereReact原生ListView:删除时删除了错误的行

但是,没有一个答案/评论是令人满意的。他们要么告诉你克隆没有解决问题的数据,要么在ListView上设置一个唯一的键来解决问题,但创建一个新的键。

当您在ListView上设置唯一键并在删除后更新它时,整个视图会再次呈现并滚动到顶部。滚动列表以删除项目的用户期望列表保持其位置。

这里是一个人为的例子,使用的是什么我将承担为克隆数据的正确方法:

var ListViewExample = React.createClass({ 

    getInitialState() { 
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => { 
     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 <TouchableOpacity onPress={this._deleteRow} 
     style={{height: 60, flex: 1, borderBottomWidth: 1}}> 
     <Text>{rowData}</Text> 
    </TouchableOpacity> 
    }, 

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow} 
     /> 
    ); 
    } 

}); 

而且here你可以看到它的GIF在哪里不对劲获得连续的删除动作。另一种方法,设置ListView的key属性会删除正确的行,但正如我所说的将导致列表滚动到顶部。

UPDATE

我已经接受了纳德的答案下面但这似乎并没有被正确的做法,因为它不会对数据源调用rowHasChanged。它也不符合ListView文档。它没有解决我的问题,所以我会离开它标记为答案,但我有一种感觉,这是一种解决方法,而不是正确的解决方案

另一个更新

好吧,这是尴尬的,但我的rowHasChanged功能是缺少return声明。我想我的咖啡中含有过多的ES6糖。

所以最后,如果你搞砸了你的rowHasChanged功能,this._ds将工作出于某种原因。如果你以正确的方式开始,你应该使用类似于:

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

当更新你的dataSource

还请记住,您需要新的rows datablob。用cloneWithRows(rows.push(newRow)之类的东西来改变它将不起作用,而cloneWithRows(rows.concat([newRow])将起作用。

+0

如果帮助别人......我是......让我的列表的深拷贝克隆之前的关键所以之前cloneWithRows做new_rows = ...行] –

回答

5

我认为问题在于您正在根据以前的ListView.DataSource实例设置您的数据源。我已经建立了一个演示我正在谈论的here,并将下面的示例与另一种方法一起进行。

尝试这样做:

'use strict'; 

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

var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => { r1 !== r2 }}); 

var SampleApp = React.createClass({ 

    getInitialState() { 
    var rows = ['row 1', 'row 2', 'row 3']; 
    return { 
     dataSource: ds.cloneWithRows(rows), 
    }; 
    }, 

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

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

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow} 
     /> 
    ); 
    } 

}); 

https://rnplay.org/apps/YGBcIA

您还可以使用ROWID在renderRow以确定要删除的项目,然后在你的删除功能,重新设置数据源的状态。

查看我设置的示例here。此外,代码如下:

'use strict'; 

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

var rows = ['row 1', 'row 2', 'row 3']; 
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => { 
     r1 !== r2 
    }}); 

var SampleApp = React.createClass({ 

    getInitialState() { 

    return { 
     dataSource: ds.cloneWithRows([]), 
     rows: rows 
    }; 
    }, 

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

    _deleteRow(rowID) { 
    this.state.rows.splice(rowID, 1) 
    this.setState({ 
     dataSource: ds.cloneWithRows(this.state.rows), 
    }) 
    }, 

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

    render() { 
    return (
     <ListView 
     dataSource={this.state.dataSource} 
     renderRow={this.renderRow} 
     /> 
    ); 
    } 

}); 
var styles = StyleSheet.create({ 
    container: { 
    flex: 1, 
    justifyContent: 'center', 
    alignItems: 'center', 
    backgroundColor: '#F5FCFF', 
    }, 
    welcome: { 
    fontSize: 28, 
    textAlign: 'center', 
    margin: 10, 
    }, 
    instructions: { 
    textAlign: 'center', 
    color: '#333333', 
    fontSize: 19, 
    marginBottom: 5, 
    }, 
}); 

AppRegistry.registerComponent('SampleApp',() => SampleApp); 
+1

你是对的。谢谢! React Native网站上的电影教程以及ListView的文档按照我所做的方式设置了新状态,使用'dataSource:this.state.dataSource.cloneWithRows',所以我想我没有想到按照你的方式去做之前。再次,谢谢! –

+0

真棒,是在ListView的文档是好的,但他们不是他们最好的。我现在正在处理一个请求,希望他们会认为这很有道理! –

+0

我刚刚注意到,使用上面描述的方法,'rowHasChanged'没有被调用。您可以通过在其中放置控制台日志来验证它。这不可能是预期的行为,可以吗?正如我所说,React Native文档使用'this.state.dataSource.cloneWithRows'更新状态,而不是在某些全局'ds'对象上使用'ds.cloneWithRows'。 –