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?
EDIT:
It's working now but I have to have chat-log set to "overflow:scroll" otherwise it doesn't kick in.. for example "overflow:auto" doesn't work.
When it DOES work, it also doesn't scroll to the very end of the box but slightly above.
Any solution to this?
Solved it by adding this code:
useEffect(() => {
const chatLogElement = messagesEndRef.current;
const currentScrollTop = chatLogElement.scrollTop;
const targetScrollTop = chatLogElement.scrollHeight - chatLogElement.clientHeight;
const scrollDiff = targetScrollTop - currentScrollTop;
let startTime;
function scroll(timestamp) {
if (!startTime) {
startTime = timestamp;
}
const elapsedTime = timestamp - startTime;
const progress = elapsedTime / 200;
chatLogElement.scrollTop = currentScrollTop + (scrollDiff * progress);
if (progress < 1) {
window.requestAnimationFrame(scroll);
}
}
window.requestAnimationFrame(scroll);
}, [chatLog.length]);
Now it works, even when overflow is set to "auto"