Home > Enterprise >  useEffect to scroll to bottom not working
useEffect to scroll to bottom not working

Time:02-01

So this is my code, testing out a chatbot. useEffect is not working when I refresh, the automatic scroll doesn't work when new message is being received or sent.. what am I missing?

import './App.css';
import './normal.css';
import { useState, useRef, useEffect } from 'react';

function App() {

  const messagesEndRef = useRef(null);
  const [input, setInput] = useState("");
  const [chatLog, setChatLog] = useState([{
    user: "gpt",
    message: "Hello World"
  }])

  function clearChat(){
    setChatLog([]);
  }

useEffect(() => {
    messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
  }, [chatLog]);

  async function handleSubmit(e){
    e.preventDefault();
    let chatLogNew = [...chatLog, { user: "me", message: `${input}` } ]
    await setInput("");
    setChatLog(chatLogNew)

    const messages = chatLogNew.map((message) => message.message).join("\n")

    const response = await fetch("http://localhost:3080/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        message: messages
      })
    });
    const data = await response.json();
    setChatLog([...chatLogNew, { user: "gpt", message: `${data.message}`}])

  }

  return (
    <div className="App">
      <aside className ="sidemenu">
        <div className="title">
            <h1>Title</h1>
        </div>
        <div className="side-menu-button" onClick={clearChat}>
          <span> </span>
          New chat
        </div>
      </aside>
      <section className="chatbox">
        <div className="chat-log">
          {chatLog.map((message, index) => (
            <ChatMessage key={index} message={message} />
          ))}
        </div>
        <div ref={messagesEndRef} />
        <div className="chat-input-holder">
            <form onSubmit={handleSubmit}>
              <input 
              rows="1"
              value={input}
              onChange={(e)=> setInput(e.target.value)}
              className="chat-input-textarea">
              </input>
            </form>
        </div>
      </section>
    </div>
  );
}

const ChatMessage = ({ message }) => {
  return (
    <div className={`chat-message ${message.user === "gpt" && "chatgpt"}`}>
            <div className="chat-message-center">
            <div className={`avatar ${message.user === "gpt" && "chatgpt"}`}>

              {message.user === "gpt" && "AI"}

              {message.user === "me" && "Me"}
              
              </div>
              <div className="message">
                {message.message}
              </div>
            </div>
          </div>
  )
}

export default App;

Defined the messagesEndRef and inserted the useEffect and put in the dummy div to the last rendered message.

Any ideas? Am I formatting it wrong?

CodePudding user response:

Instead of using chatLog in the second argument array of useEffect(), use [JSON.stringify(chatLog)] or chatLog.length

useEffect(() => {
    messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
  }, [JSON.stringify(chatLog)]);

CodePudding user response:

scrollIntoViewOptions are:

behavior (Optional)

Defines the transition animation. One of auto or smooth. Defaults to auto.

block (Optional)

Defines vertical alignment. One of start, center, end, or nearest. Defaults to start.

inline (Optional)

Defines horizontal alignment. One of start, center, end, or nearest. Defaults to nearest.

So, you will need to add block option to end.

useEffect(() => {
    messagesEndRef.current.scrollIntoView({ behavior: "smooth", block: "end" })
  }, [chatLog]);
  • Related