【问题标题】:ListView is not re-rendering after dataSource has been updated更新数据源后,ListView 不会重新渲染
【发布时间】:2017-12-13 14:00:59
【问题描述】:

我正在尝试在 react-native 中实现一个 todo 应用程序,它具有 addTodo、removeTodo、markCompleted todos 等功能。添加待办事项后,当我按下 markComplete 文本时,listView 不会重新呈现,如果我重新加载应用程序,它会显示预期结果。我正在使用 Firebase 数据库从中获取我的待办事项。

基本上,当我单击markComplete 时,我正在更新我的listView 数据源中的一个属性。一切正常,只要我在 UI 上按下 markComplete 或 Completed 按钮,就会重新渲染 listView。我已经尝试了相关问题中建议的一些解决方案,但无法正常工作。

更具体地说:请看下面的代码注释 // 当待办事项发生变化时。当我更改 items 数组中的某些内容时,我正在这些代码行中更新我的数据源。

以下是我的应用程序 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

【问题讨论】:

    标签: javascript listview firebase react-native firebase-realtime-database


    【解决方案1】:

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

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

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

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

    如需更详细的解释,This answer 可能会对您有所帮助。

    【讨论】:

    • 我在发布此问题之前尝试了该代码,但对我没有用。当你有数组时,我读到它可以工作,但对于对象数组,它必须有所不同。如果你能提供一些有用的代码,那就太好了。
    • 我还在为这个 ListView 重新渲染问题而苦苦挣扎,我已经尝试了在 stackoverflow 的其他帖子中建议的更多解决方案。有没有人能指出这段代码有什么问题?
    猜你喜欢
    • 1970-01-01
    • 2017-02-04
    • 2016-05-20
    • 2017-11-16
    • 2019-06-17
    • 1970-01-01
    • 1970-01-01
    • 2021-05-19
    • 2022-01-26
    相关资源
    最近更新 更多