Проблемы с загрузкой более ранних сообщений в приложении чата GiftedChat с поддержкой React с Firebase

Я работал над приложением чата с использованием Gifted-Chat и базы данных Firebase RealTime (и запускал его с Expo). На данный момент основной обмен сообщениями работает, но я пытаюсь разрешить приложению загружать более ранние сообщения, когда пользователь прокручивает страницу вверх и нажимает появившуюся кнопку (мне известно о поддержке GiftedChat для этого). К сожалению, у меня возникли проблемы с этим, и я немного озадачен.

Я знаю две отдельные проблемы, с которыми я столкнулся.

  1. Нажатие кнопки loadEarlier дает мне undefined is not a function (near '...this.setState...' ошибку времени выполнения (очевидно, что-то не так с функцией скелета, которую я туда поместил).
  2. Более серьезная проблема заключается в том, что я до сих пор не понимаю, как загрузить n сообщений до того, как загружаются самые старые сообщения. Я просмотрел пример GiftedChat и этот пост для помощи, но должен признаться, что я все еще потерялся (лучшее, что я могу понять, это то, что мне нужно отсортировать сообщения, возможно, по метке времени, каким-то образом получить правильный диапазон, затем проанализировать их и добавить их в массив сообщений в состоянии, но я не могу понять, как это сделать, особенно в более поздних частях).

Соответствующие части кода для моего экрана чата приведены ниже, как и скриншот структуры моей базы данных firebase. Буду признателен за любую помощь по обоим этим вопросам.

// Your run of the mill React-Native imports.
import React, { Component } from 'react';
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import * as firebase from 'firebase';
// Our custom components.
import { Input } from '../components/Input';
import { Button } from '../components/Button';
import { BotButton } from '../components/BotButton';
// Array of potential bot responses. Might be a fancy schmancy Markov
// chain like thing in the future.
import {botResponses} from '../Constants.js';
// Gifted-chat import. The library takes care of fun stuff like
// rendering message bubbles and having a message composer.
import { GiftedChat } from 'react-native-gifted-chat';
// To keep keyboard from covering up text input.
import { KeyboardAvoidingView } from 'react-native';
// Because keyboard avoiding behavior is platform specific.
import {Platform} from 'react-native';

console.disableYellowBox = true;

class Chat extends Component {
    state = {
        messages: [],
        isLoadingEarlier: false,
    };

    // Reference to where in Firebase DB messages will be stored.
    get ref() {
        return firebase.database().ref('messages');
    }

    onLoadEarlier() {
        this.setState((previousState) => {
          return {
            isLoadingEarlier: true,
          };
        });
        console.log(this.state.isLoadingEarlier)
        this.setState((previousState) => {
            return {
                isLoadingEarlier: false,
            };
        });
    }

    // Get last 20 messages, any incoming messages, and send them to parse.
    on = callback =>
        this.ref
          .limitToLast(20)
          .on('child_added', snapshot => callback(this.parse(snapshot)));
    parse = snapshot => {
        // Return whatever is associated with snapshot.
        const { timestamp: numberStamp, text, user } = snapshot.val();
        const { key: _id } = snapshot;
        // Convert timestamp to JS date object.
        const timestamp = new Date(numberStamp);
        // Create object for Gifted Chat. id is unique.
        const message = {
            _id,
            timestamp,
            text,
            user,
        };
        return message;
    };
    // To unsubscribe from database
    off() {
        this.ref.off();
    }

    // Helper function to get user UID.
    get uid() {
        return (firebase.auth().currentUser || {}).uid;
    }
    // Get timestamp for saving messages.
    get timestamp() {
        return firebase.database.ServerValue.TIMESTAMP;
    }

    // Helper function that takes array of messages and prepares all of
    // them to be sent.
    send = messages => {
        for (let i = 0; i < messages.length; i++) {
            const { text, user } = messages[i];
            const message = {
                text,
                user,
                timestamp: this.timestamp,
            };
            this.append(message);
        }
    };

    // Save message objects. Actually sends them to server.
    append = message => this.ref.push(message);

    // When we open the chat, start looking for messages.
    componentDidMount() {
        this.on(message =>
          this.setState(previousState => ({
              messages: GiftedChat.append(previousState.messages, message),
          }))
        );
    }
    get user() {
        // Return name and UID for GiftedChat to parse
        return {
            name: this.props.navigation.state.params.name,
            _id: this.uid,
        };
    }
    // Unsubscribe when we close the chat screen.
    componentWillUnmount() {
        this.off();
    }

    render() {
        return (
        <View>
            <GiftedChat
                loadEarlier={true}
                onLoadEarlier={this.onLoadEarlier}
                isLoadingEarlier={this.state.isLoadingEarlier}
                messages={this.state.messages}
                onSend={this.send}
                user={this.user}
            />
         </View>
        );
    }

}
export default Chat;

Скриншот БД




Ответы (3)


Для вашей первой проблемы вы должны объявить свой onLoadEarlier с помощью => функции, чтобы получить текущий экземпляр this, т.е. ваш код должен выглядеть следующим образом:

onLoadEarlier = () => {
    this.setState((previousState) => {
      return {
        isLoadingEarlier: true,
      };
    }, () => {
       console.log(this.state.isLoadingEarlier)
       this.setState((previousState) => {
          return {
             isLoadingEarlier: false,
          };
       });
    }); 
}

Кроме того, setState является асинхронным по своей природе, поэтому вам лучше полагаться на второй параметр setState, то есть обратный вызов, чтобы гарантировать, что следующие строки кода выполняются синхронно.

Наконец, если вы используете синтаксис class, вы должны объявить состояние в constructor, как показано ниже:

class Chat extends Component {
   constructor (props) {
      super (props);
      state = {
         messages: [],
         isLoadingEarlier: false,
      };
   }
  ......

    onLoadEarlier = () => {
      this.setState((previousState) => {
        return {
          isLoadingEarlier: true,
        };
      }, () => {
         console.log(this.state.isLoadingEarlier)
         this.setState((previousState) => {
            return {
               isLoadingEarlier: false,
            };
         });
      }); 
  }
  ...
}

Что касается вашего второго ответа, я когда-нибудь обновлю ответ, чтобы помочь с этим.

Надеюсь это поможет. Удачного кодирования :)

person Suraj Malviya    schedule 28.12.2018
comment
Спасибо, все работает. Я все еще новичок в React-native (первый проект), поэтому мне интересно, почему я должен объявлять состояние в конструкторе? - person Fake Name; 29.12.2018
comment
Это может помочь вам понять, как члены класса определены и объявлены. - person Suraj Malviya; 29.12.2018
comment
Спасибо, я не понимал, насколько сложен javascript. Однако после просмотра страницы я все еще не понимаю, почему для объявления состояния нужно использовать конструктор, учитывая, что код работает для меня после применения ваших первых исправлений без добавления конструктора. - person Fake Name; 30.12.2018
comment
Извините, неважно, я думаю, что наконец-то в основном понимаю всю эту связывающую вещь. Если у вас будет возможность узнать, как загрузить старые сообщения, я был бы очень признателен. - person Fake Name; 31.12.2018

Для загрузки последних сообщений из firebase я рекомендую использовать функцию limitToLast по вашей ссылке. После этого вы должны упорядочить результаты по дате, прежде чем вызывать добавление в чате подарков.

person Omri Haim    schedule 10.03.2019
comment
Проблема, с которой я столкнулся, заключалась в том, чтобы выяснить, как избежать повторной загрузки всех сообщений, которые у меня уже есть. Есть ли способ сделать это? - person Fake Name; 11.03.2019

Что касается второго вопроса, то он должен быть таким же, как и этот. Чем отличаются Firebase on и once?

Вы можете использовать функцию фильтра в Firebase, например, используя поле createdAt для сравнения с последним загруженным сообщением, чтобы загрузить больше.

person Lap Tran    schedule 04.05.2019