每当添加新数据时,FlatList 都会多次重新呈现来自 Firebase 的数据

FlatList rerenders data from Firebase multiple times whenever new data is added

我正在尝试使用本机反应和 firebase 构建聊天应用程序界面,但在更新从我的数据库中获取的数据时遇到问题。我可以将现有数据加载到我正在使用的 Flatlist 中:

const firebaseApp = firebase.initializeApp(firebaseConfig);
let db = firebaseApp.database();
let ref = db.ref("/room");

 componentDidMount() {
      ref.on('value', function(snapshot) {
        snapshot.forEach(function (childSnapshot){
          childData.push(childSnapshot.val());
        });
        this.setState({
          messages: childData
        });
       messageCount = snapshot.numChildren();
     }.bind(this));
 }

Flatlist代码:

<KeyboardAvoidingView style={styles.inputContainer}>
      <FlatList
          data={this.state.messages}
          // renderItem={({item}) =><Text>{item.contents}</Text>}
          keyExtractor = {item => item.timestamp}
          renderItem={({item}) => <Bubble style={{margin: 10}} 
          color="#FFC800" arrowPosition='right'>{item.contents}</Bubble>}
            />}
        />
        <Button onPress={()=>{this.onPressButton()}} title="Send">Click me</Button>      
      </KeyboardAvoidingView>

这是我将文本输入中的文本添加到数据库的地方

function addMessage(messageText) {
  messageCount++;
  firebase.database().ref('room/'+messageCount).set({
      contents: messageText,
      timestamp: firebase.database.ServerValue.TIMESTAMP
  });
}

该代码给出了我想要的以下结果(忽略糟糕的样式):

但是每当我尝试发送消息时,它都会正确地添加到数据库中,但是平面列表会更新以显示:

其中多次呈现上一条消息和新消息。将数据库中的新项目呈现到平面列表中的最佳方法是什么?

查看你的代码我找不到你在哪里初始化你的 childData

假设您的数据库是正确的,那么看起来您在将其分配给您的州之前没有清理childData

this.setState({
  messages: childData
});

因此,当您的代码 运行 此推送 childData.push(childSnapshot.val()); 时,它会将所有新内容添加到您的数组并保留旧内容。

示例:

第一状态:

// snapshot.val() contains ['Hello']
childData = ['Hello']

提交的文本:World!

更新状态:

// snapshot.val() contains ['Hello', 'World']
childData = ['Hello', 'Hello', 'World!']

解决这个问题的一个建议是只给数组分配新值,所以不用 push,你可以这样做 childData = snapshot.val()

我还建议您使用一些 console.log 调试您的代码,以便了解 snapshot.val()childDatathis.state.messages[=28 正在检索的内容=]

希望对您有所帮助

编辑: 再次阅读,一个可能的问题也可能是因为 JS 是同步的,所以你的 setState 在你的 forEach 完成之前被调用。一个解决方案可以使用 async/await https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

像这样:

ref.on('value', async (snapshot) => {
  const childData = await snapshot.map((childSnapshot) => {
   childSnapshot.val();
  });
  this.setState({
    messages: childData
  });
 (...)

以上代码为例

希望对您有所帮助