当行数据更改时,如何强制ReactNative ListView更新行? [英] How to force ReactNative ListView to update a row when row-data change?

查看:109
本文介绍了当行数据更改时,如何强制ReactNative ListView更新行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在数据更新方面,ReactNative的ListView有点奇怪.

ReactNative's ListView is little bit odd when it comes to data updates.

我有一个对象数组(名称:Item),具有3个属性:

I have an array of objects (name: Item), that have 3 properties:

  • id(int)
  • 名称(字符串)
  • 已启用(布尔)

项目数组显示在ListView中.每行显示该项目的名称和一个表示启用状态的复选标记(红色为true,灰色为false).在我的示例中,它简化为简单的字符串"true"/"false".

The array of items is displayed in a ListView. Each row shows the name of the item and a checkmark representing the enabled-state (red on true, gray on false). In my example it is simplified to a simple string "true"/"false".

每行都带有一个onPress-EventHandler嵌入到TouchableOpacity中,该onPress-EventHandler可以切换所表示项目的启用状态.

Each row is embedded in a TouchableOpacity with a onPress-EventHandler, that toggles the enabled-state of the represented item.

不幸的是,ListView不会更新该行.

Unfortunately, the ListView won't update the row.

在调试rowHasChanged-Event时,事实证明rowHasChanged-Event永远无法确定更改后的状态,因为两行在切换启用属性后都已经获得了新状态,而无需对UI进行任何更新.

While debugging the rowHasChanged-Event, it turns out that the rowHasChanged-Event could never determine the changed state because both rows already get the new state after toggling the enabled-property without any update to the UI.

export default class Item {

  constructor(id, name, enabled) {
    this._id = id;
    this._name = name;
    this._enabled = enabled;
  }

  get id() {return this._id}

  get name() {return this._name}
  set name(value) {this._name = value}

  get enabled() {return this._enabled}
  set enabled(value) {this._enabled = value}
  
}

ListView:

'use strict';

import React, {PropTypes, Component} from 'react';

import {View,TouchableOpacity, TouchableHighlight,
  Text, ListView, StyleSheet} from 'react-native';

import Item from './Item';

class ItemListView extends Component {

  constructor(props) {
    super(props);

    this.updateListView = this.updateListView.bind(this);
    this.toggleItemEnabled = this.toggleItemEnabled.bind(this);

    this.state = {
      dataSource: new ListView.DataSource({
                rowHasChanged: (row1, row2) => row1 != row2
            }),
    };
  }

  componentDidMount() {
    this.updateListView(this.props.items)
  }

  componentWillReceiveProps(nextProps) {
    this.updateListView(nextProps.items)
    }

  updateListView(items) {
    this.setState({
            dataSource: this.state.dataSource.cloneWithRows(
                items.slice() // copy items to a new array
            ),
        });
  }

  toggleItemEnabled(item) {
    item.enabled = !item.enabled;

    this.updateListView(this.props.items);
  }

  renderItem(item) {
    console.log('Render', item.enabled, item.name)
    return (
      <TouchableOpacity style={{flex:1}} onPress={ () => this.toggleItemEnabled(item) }>
        <View style={s.row}>
              <Text>{item.name + ' ' + item.enabled}</Text>
          </View>
      </TouchableOpacity>
    )
  }

  render() {
    return (
      <View style={s.container}>
        <ListView
          style={s.listView}
          dataSource={this.state.dataSource}
          renderRow={(item) => this.renderItem(item)}
        />
      </View>
    )
  }

}

ItemListView.propTypes = {
  items: PropTypes.array
};

ItemListView.defaultProps = {
  items: [],
};

export default ItemListView;


const s = StyleSheet.create({
  container: {
  },

  row: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginTop: 12,
    paddingBottom: 12,
    paddingHorizontal: 30,
    borderBottomWidth: 1,
    borderBottomColor: 'gray',
  },

  innerContainer: {
    alignItems: 'center',
  },

});

如何解决这种行为,即当行数据更改时ListView会更新?

How to fix this behaviour, that the ListView gets updated when row-data changes?

推荐答案

我认为问题在于更新itemListView数据源没有得到更新.传递给toggleItemEnableditem属于state.datasource,而您正在设置props.items

I think the problem is the ListView data source is not getting updated when you update item. The item passed to toggleItemEnabled belongs to state.datasource whereas you are setting the datasource for props.items

解决方案1 ​​

克隆行并再次设置行

toggleItemEnabled(item, sectionId, rowId) {
    newItems = this.props.items.slice();
    newItems[rowId].enabled = !newItems[rowId].enabled;

    this.updateListView(newItems);
}

解决方案2

这是我希望的方式.我通常为Row创建一个单独的组件 所以你应该重构如下

This is the way I would prefer. I generally create a separate component for Row So you should refactor as follows

创建一个新的Row.js

constructor(props) {
    super(props);

    this.state = {
       enabled: false;
    };
 }


toggleItemEnabled() {
    this.setState({enabled: !this.state.enabled});

}

render() {
 item = this.props.data;
 return (
      <TouchableOpacity style={{flex:1}} onPress={ () => this.toggleItemEnabled() }>
        <View style={s.row}>
              <Text>{item.name + ' ' + this.state.enabled}</Text>
          </View>
      </TouchableOpacity>
    )

}

然后在渲染行方法的主组件中将此行组件称为

Then in your main component in render row method call this Row component

renderItem(item) {
     return(
         <Row data={item} />
     );
 }

这篇关于当行数据更改时,如何强制ReactNative ListView更新行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆