【问题标题】:React-Native Updating List View DataSourceReact-Native 更新列表视图数据源
【发布时间】:2015-10-22 16:33:50
【问题描述】:

我有一个使用 react-native 制作的 iOS 应用。 Game 类包含一个 ListView 组件。我在构造函数中设置状态并包含dataSource。我现在有一个硬编码的数据数组,我存储在不同的状态属性 (this.state.ds) 中。然后在componentDidMount 中,我使用cloneWithRows 方法克隆我的this.state.ds 作为视图的数据源。就 ListViews 而言,这是相当标准的,并且运行良好。代码如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

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

class Games extends React.Component {
constructor(props) {
    super(props);
    var ds = new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 != r2
    });
    this.state = {
        ds: [
            { AwayTeam: "TeamA", HomeTeam: "TeamB", Selection: "AwayTeam" },
            { AwayTeam: "TeamC", HomeTeam: "TeamD", Selection: "HomeTeam" }
        ],
        dataSource: ds
    };
}

componentDidMount() {
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(this.state.ds)
    });
}
pressRow(rowData) {
    var newDs = [];
    newDs = this.state.ds;
    newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(newDs)
    });
}

renderRow(rowData) {
    return (
        <TouchableHighlight
            onPress={() => this.pressRow(rowData)}
            underlayColor="#ddd"
        >
            <View style={styles.row}>
                <Text style={{ fontSize: 18 }}>
                    {rowData.AwayTeam} @ {rowData.HomeTeam}{" "}
                </Text>
                <View style={{ flex: 1 }}>
                    <Text style={styles.selectionText}>
                        {rowData[rowData.Selection]}
                    </Text>
                </View>
            </View>
        </TouchableHighlight>
    );
}
render() {
    return (
        <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRow.bind(this)}
        />
    );
}
}
var styles = StyleSheet.create({
 row: {
    flex: 1,
    flexDirection: "row",
    padding: 18,
    borderBottomWidth: 1,
    borderColor: "#d7d7d7"
},
selectionText: {
    fontSize: 15,
    paddingTop: 3,
    color: "#b5b5b5",
    textAlign: "right"
}
});


module.exports = Games

我遇到的问题来自 pressRow 方法。当用户按下该行时,我希望选择更改并让它在设备上呈现更改。通过一些调试,我注意到即使我更改了newDs 数组中对象的Selection 属性,this.state.ds 中的对象的相同属性也会更改,并且类似地更改this.state.dataSource._dataBlob.s1 中的对象。通过进一步调试,我发现由于其他数组发生了变化,ListView 的 DataSource 对象无法识别更改,因为当我设置状态并调用 rowHasChanged 时,它正在克隆的数组与数组 this.state.dataSource._dataBlob.s1 匹配并且所以它看起来不像变化,也不会重新渲染。

有什么想法吗?

【问题讨论】:

    标签: javascript ios arrays datasource react-native


    【解决方案1】:

    试试这个:

    pressRow(rowData){
    
        var newDs = [];
        newDs = this.state.ds.slice();
        newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(newDs)
        })
    
    }
    

    这应该制作数组的副本,然后可以独立于this.state.ds 中的原始数组进行修改。

    【讨论】:

    • 非常感谢!为了让它发挥作用,我还必须做另外一个步骤。出于某种原因,我不得不“清除”我试图改变的对象。所以我只是让newDs[0] 等于一个新对象,并给它我试图改变的对象的属性。当然,我更改了Selection 属性。
    • this.state.ds.slice() 更改为 this.state.ds.slice(0) 以获得更好的性能。
    • @TheNickyYo - 为什么?文档说 undefined 无论如何都被视为 0?
    • this.state.ds.slice() 不是函数
    【解决方案2】:

    如果有人像我一样遇到这个问题,这里是完整的解决方案。

    基本上,ListView 组件似乎存在错误,您需要重建数据源中更改的每个项目,以便 ListView 重新绘制它。

    首先,创建数据源和数组的副本并将它们保存到状态。在我的例子中,foods 是数组。

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

    当您想更改数据源中的某些内容时,复制您保存到状态的数组,使用更改重建项目,然后将更改后的新数组保存回状态(数据库和数据源) .

    onCollapse(rowID: number) {
        console.log("rowID", rowID);
        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,
        });
    }
    

    【讨论】:

    • 与其手动重建整个对象,您应该可以这样做:var copy = Object.assign({}, obj); 每个 MDN 文档:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    • 或者如果你更喜欢 ES6 语法:newRows[rowId] = { ...this._sampleData[rowId], favorited: rowData.favorited ? false : true }
    • 可以用 this.db 代替 this.state.db 更好吗?
    【解决方案3】:

    react 足够聪明,可以检测 dataSource 的变化以及是否应该重新渲染列表。如果要更新 listView,请创建新对象而不是更新现有对象的属性。 代码看起来像这样:

    let newArray = this._rows.slice();
    newArray[rowID] = {
      ...this._rows[rowID],
      Selection: !this._rows[rowID].Selection,
    };
    this._rows = newArray;
    
    let newDataSource = this.ds.cloneWithRows(newArray);
    this.setState({
      dataSource: newDataSource
    });
    

    你可以阅读更多类似的issue on Github

    【讨论】:

      猜你喜欢
      • 2017-03-03
      • 1970-01-01
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      相关资源
      最近更新 更多