Search code examples
reactjsfirebasejsxjs-scrollintoview

scrollIntoView does not work on first load and after each refresh


I am seeing a very strange behavior in my chat. Once the chat is opened, the scroll moves only a tiny bit down only when there are images in the chat. When there is only text, it goes all the way down. Also, if I close the chat and open it again, the scroll goes all the way down regardless of the content. However, If I refresh the page, the scroll returns to its weird behavior. I am puzzled as to why this is happening. Here's my code:

Here's how the chat starts:

startChat () { 
       
    
    document.getElementById("myForm").style.display = "block";       
   
    
        const ref = firebase.firestore().collection('Chats').doc(this.state.uid).collection('Messages');

        const query = ref.orderBy('timestamp', 'desc').limit(10)



        this.unsubFromMessages = query.onSnapshot((snapshot) => {                            

                            if (snapshot.empty) {

                                console.log('No matching documents.');
                                
                                firebase.firestore().collection('Chats').doc(this.state.uid).
                                set({
                                    name: this.state.displayName,
                                    uid: this.state.uid,
                                    email: this.state.email
                                }).then(console.log("info saved"))
                                .catch((error) => {
                                    console.log("Error saving info to document: ", error);
                                });
                            }
                                                  
                                snapshot.docChanges().reverse().forEach((change) => {                                  
                                  

                                if (change.type === 'removed') {
                    
                                console.log(change.doc.data().content)
                    
                                } 
                                
                                
                                else if (change.type === 'added') {                
                                
                                  this.setState(state => {
                                    const messages = [...state.messages, {id: change.doc.id, body: change.doc.data()}]
                                    return {
                                        messages
                                    }
                    
                                })    
                                
                                setTimeout( this.scrollToBottom(), 2000)                                
                                
                                
                                
                                } 
                                
                                else if (change.type === 'modified') { 
                                    
                                                                        
                                    const filteredMessages = this.state.messages.filter(message => message.body.allowed === "yes")

                                   this.setState(state => {
                                      const messages = [...filteredMessages, {id: change.doc.id, body: change.doc.data()}]
                                      return {
                                          messages
                                      }
                      
                                  })    
                                  
                                  setTimeout( this.scrollToBottom(), 2000)
                                  
                                  
                                  
                                  } 
                    
                                                                  
                                });
                                }, (error) => {console.log(error)});             
                    
                                
    }

Here's the scroll function:

scrollToBottom = () => {     

        this.myRef.current.scrollIntoView({ behavior: "smooth" });      
       

      }

Here's the JSX of the chat:

<div className="form-popup" id="myForm">
                    <form className="form-container" onSubmit={this.chatFormSubmit}>

                        <h1>Chat</h1>

                        <label htmlFor="msg"><b>Message</b></label>


                        <div className="chatArea" id='messages'>
                            
                       
                            {
                               
                                this.state.messages.map((message, index) => {
                                return message.body.uid === this.state.uid && !message.body.imageUrl
                                ?
                                <p className="message-sent" key={index}>{message.body.content}</p>
                                : 
                                message.body.uid === this.state.uid && message.body.imageUrl 
                                ?
                                <img src={message.body.imageUrl} className="message-sent" key={index}></img>
                                :
                                <p className="message-received" key={index}>{message.body.content}</p>

                                    

                                
                                })
                            
                            }      

                            <div style={{ float:"left", clear: "both" }}
                               ref={this.myRef}>
                            </div>               
                           

                        </div>

And if the functions for closing and submitting messages to the chat are of any use, here they are:

closeForm() {
        document.getElementById("myForm").style.display = "none";
        this.setState({messages: []})
        this.unsubFromMessages();        
    }


chatFormSubmit(e) {
                  
        e.preventDefault();

        this.setState({ writeError: null }); 

        firebase.firestore()
        .collection('Chats')
        .doc(this.state.uid)
        .collection('Messages')  
        .doc()
        .set({
            docId: this.state.docId,
            content: this.chatArea.current.value,
            allowed: "yes",
            timestamp: new Date(),
            uid: this.state.uid,
            name: this.state.displayName,
            email: this.state.email
            }, { merge: true })
            .catch((error) => {
            this.setState({ writeError: error.message });
        })
            .then(this.chatArea.current.value = '')





    }

Solution

  • Again, I figured it out myself. Instead of calling "this.scrollToBottom()" in setTimeout, I should have simply passed it like this setTimeout( this.scrollToBottom, 2000). That is why setTimeout was not working and the scroll stopped half way. Credit goes to Felix Kling's comment in ReactJS: setTimeout() not working?.