react-nativegoogle-cloud-firestorereact-native-gifted-chat

React Native Gifted Chat + Firestore not showing messages correctly?


I am trying to create a chat feature in my react native app. I am using react-native-gifted-chat and saving the messages in firestore. Here is the behavior that is occurring:

enter image description here

When I send a message, ALL the messages re render, some of them are duplicates, as you can see I only have 3 messages sent so far, but all these duplicates are making me wonder why the entire thing is re-rendering and why there are duplicates when it does re-render.

The code:

class Chat extends React.Component {
    
    constructor(props) {
        super(props)
        this.state = {
            messages: [],
            currentUser: null,
            isLoading: true,
            messageID: ""
        }
    }
    //---------------------------------------------------------------
    async componentDidMount (){
        // get user info from firestore
        let userUID = Firebase.auth().currentUser.uid

        await Firebase.firestore().collection("users").doc(userUID).get()
        .then(doc => {
            data = doc.data()
            this.setState({
                currentUser: {
                    name: data.username,
                    avatar: data.profilePic,
                    _id: doc.id,
                },
            })
        })

        const messages = []

        await Firebase.firestore().collection("chat")
        .orderBy("createdAt", "desc")
        .limit(50)
        .onSnapshot(querySnapshot => {
            querySnapshot.forEach((res) => {
                const { 
                    user,
                    text,
                    createdAt,
                    } = res.data();
    
                    messages.push({
                        key: res._id,
                        user,
                        text,
                        createdAt,
                    });
            })

            this.setState({
                messages,
                isLoading: false, 
            });
        })
    }

    //Load 50 more messages when the user scrolls

    //

    //Add a message to firestore
    onSend = async(message) => {

        await Firebase.firestore().collection("chat")
        .add({
            user: {
                _id: this.state.currentUser._id,
                name: this.state.currentUser.name,
                avatar: this.state.currentUser.avatar,
            },
            
        })
        .then(ref => this.setState({messageID: ref.id}))

        await Firebase.firestore().collection("chat")
        .doc(this.state.messageID)
        .set({
            _id: this.state.messageID,
            text: message[0].text,
            createdAt: message[0].createdAt
        }, { merge: true })

    }

    render() {
        if(this.state.isLoading){
            return(
              <View style = {{backgroundColor: '#000000', flex: 1}}>
                <ActivityIndicator size="large" color="#9E9E9E"/>
              </View>
            )
        }   
        return (
            <View style={{backgroundColor: '#000000', flex: 1}}>
                <GiftedChat
                    showUserAvatar={true}
                    renderUsernameOnMessage={true}
                    messages={this.state.messages}
                    onSend={message => this.onSend(message)}
                    scrollToBottom
                />
            </View>
            
        )
        
    }
}

Some notes:

  1. Every time the component mounts, the messages array pushes the messages to the state array.
  2. The component mounts when I send a message, thus re-rendering the array of messages.
  3. Each message ID is unique and generated by firebase using "Add".

Let me know how I can fix this issue.


Solution

  • Duplication is because of just single line

        const messages = []
    

    Move this line inside listener, i.e.onSnapShot()

        await Firebase.firestore().collection("chat")
            .orderBy("createdAt", "desc")
            .limit(50)
            .onSnapshot(querySnapshot => {
                const messages = []
                // rest of your code which is having forEach loop
            });
    

    The issue was that messages object was created only once when the component loaded, and you were pushing elements to that object only.