更新dataSource后,ListView不会重新呈现

时间:2022-09-05 20:12:08

I am trying to implement a todo app in react-native with features like addTodo, removeTodo, markCompleted todos. After adding todos, when I press on markComplete text, the listView is not re-rendering, if I reload the app it displays expected results. I am using Firebase database to fetch my todos from.

我正在尝试使用addTodo,removeTodo,markCompleted todos等功能实现一个反应原生的todo应用程序。添加待办事项后,当我按下markComplete文本时,listView不会重新渲染,如果我重新加载应用程序,它会显示预期结果。我正在使用Firebase数据库来获取我的待办事项。

Basically, I am updating a property in my listView datasource when I click on markComplete. Everything is working fine expect the re-rendering of listView whenever I press markComplete or Completed buttons on UI. I have tried a few solutions suggested in related question, I couldnt get it working.

基本上,当我点击markComplete时,我正在更新listView数据源中的属性。每当我按下UI上的markComplete或Completed按钮时,一切都工作正常,期望重新呈现listView。我在相关问题中尝试了一些解决方案,我无法使其正常工作。

To be more specific: please look at code below comment // When a todo is changed. I am updating my datasource in those lines of code when I changes something in items array.

更具体一点:请查看以下代码注释//当todo更改时。当我更改items数组中的某些内容时,我正在更新这些代码行中的数据源。

Below is my code and snapshot of app UI.

以下是我的应用UI的代码和快照。

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  ListView,
  Text,
  View,
  TouchableHighlight,
  TextInput
} from 'react-native';

var Firebase = require('firebase');

class todoApp extends Component{
  constructor(props) {
  super(props);
  var myFirebaseRef = new Firebase('[![enter image description here][1]][1]database URL');
  this.itemsRef = myFirebaseRef.child('items');

  this.state = {
    newTodo: '',
    completed: false,
    todoSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2})
  };
  this.handleKey = null;
  this.items = [];
} // End of Constructor

componentDidMount() {
  // When a todo is added
  this.itemsRef.on('child_added', (dataSnapshot) => {
    this.items.push({
       id: dataSnapshot.key(),
       text: dataSnapshot.child("todo").val(), 
       completedTodo: dataSnapshot.child("completedTodo").val()
      });
    this.setState({
      todoSource: this.state.todoSource.cloneWithRows(this.items)
    });
  });

  // When a todo is removed
  this.itemsRef.on('child_removed', (dataSnapshot) => {
      this.items = this.items.filter((x) => x.id !== dataSnapshot.key());
      this.setState({
        todoSource: this.state.todoSource.cloneWithRows(this.items)
      });
  });

  // When a todo is changed
  this.itemsRef.on('child_changed', (dataSnapshot) => {
    this.items.forEach(function (value) {
    if(value["id"] == this.handleKey){

    this.items["value"]["completedTodo"]= dataSnapshot.child("completedTodo").val()
    }
    });
    this.setState({
      todoSource: this.state.todoSource.cloneWithRows(this.items)
    });
  });
}



addTodo() {
  if (this.state.newTodo !== '') {
    this.itemsRef.push({
      todo: this.state.newTodo,
      completedTodo: this.state.completed,
    });
    this.setState({
      newTodo : ''
    });
  }

console.log(this.items);
}
removeTodo(rowData) {
  this.itemsRef.child(rowData.id).remove();
}

handleCompleted(rowData){
  this.handleKey = rowData.id;
  if(rowData.completedTodo){

    this.itemsRef.child(rowData.id).update({
      completedTodo: false
    })
  }
  if(rowData.completedTodo == false){
    this.itemsRef.child(rowData.id).update({
      completedTodo: true
    })
  }

}

renderRow(rowData) {
  return (
      <View>
        <View style={styles.row}>
          <TouchableHighlight
          underlayColor='#dddddd'
          onPress={() => this.removeTodo(rowData)}>
          <Text style={styles.todoText}>{rowData.text}</Text>
          </TouchableHighlight>
          <TouchableHighlight underlayColor='#dddddd' onPress={() => this.handleCompleted(rowData)}>
          {rowData.completedTodo? <Text style={styles.todoText}>Completed</Text>:<Text style={styles.todoText}>MarkCompleted</Text>}
          </TouchableHighlight>
        </View>
        <View style={styles.separator} />
      </View>

  );
}


render() {
  return (
    <View style={styles.appContainer}>
      <View style={styles.titleView}>
        <Text style={styles.titleText}>
          My Todos
        </Text>
      </View>
      <View style={styles.inputcontainer}>
        <TextInput style={styles.input} onChangeText={(text) => this.setState({newTodo: text})} value={this.state.newTodo}/>
        <TouchableHighlight
          style={styles.button}
          onPress={() => this.addTodo()}
          underlayColor='#dddddd'>
          <Text style={styles.btnText}>Add!</Text>
        </TouchableHighlight>
      </View>
      <ListView
        dataSource={this.state.todoSource}
        renderRow={this.renderRow.bind(this)} />
    </View>
  );
}


} // Main Class End

更新dataSource后,ListView不会重新呈现

1 个解决方案

#1


0  

Make sure to create new objects instead of updating the properties of existing objects.

确保创建新对象而不是更新现有对象的属性。

If you want to update listView, create new objects instead of updating the properties of existing objects.

如果要更新listView,请创建新对象,而不是更新现有对象的属性。

The below code resolved a similar issue on Github.

以下代码解决了Github上的类似问题。

let newArray = oldArray.slice();
newArray[indexToUpdate] = {
  ...oldArray[indexToUpdate],
  field: newValue,
};
let newDataSource = oldDataSource.cloneWithRows(newArray);

For more detailed explanation, This answer might help you.

有关更详细的说明,此答案可能对您有所帮助。

#1


0  

Make sure to create new objects instead of updating the properties of existing objects.

确保创建新对象而不是更新现有对象的属性。

If you want to update listView, create new objects instead of updating the properties of existing objects.

如果要更新listView,请创建新对象,而不是更新现有对象的属性。

The below code resolved a similar issue on Github.

以下代码解决了Github上的类似问题。

let newArray = oldArray.slice();
newArray[indexToUpdate] = {
  ...oldArray[indexToUpdate],
  field: newValue,
};
let newDataSource = oldDataSource.cloneWithRows(newArray);

For more detailed explanation, This answer might help you.

有关更详细的说明,此答案可能对您有所帮助。