[英]How to make a div scroll automatically in react (hooks)
I am currently making a chat application and having trouble making the div scroll to the bottom when a new chat message is added, I used the css overflow-y:auto to make it be able to scroll but can't figure out how to make it always scroll to the bottom when a new chat is loaded.我目前正在制作一个聊天应用程序,并且在添加新聊天消息时无法使 div 滚动到底部,我使用了 css overflow-y:auto 使其能够滚动但无法弄清楚如何制作加载新聊天时,它总是滚动到底部。
Here is my app.js file:这是我的 app.js 文件:
import "./App.css";
import React, { useEffect, useState ,useRef} from "react";
import io from 'socket.io-client';
let socket;
const CONNECTION_PORT = "localhost:3002/";
function App() {
//before login
const [logged, setLogged] = useState(false);
const [name,setName] = useState("");
const [room,setRoom] = useState("");
//after login
const [message,setMessage] = useState("");
const [messageList,setMessageList] = useState([]);
const ref = useRef();
useEffect(()=>{
socket = io(CONNECTION_PORT,{transports: ['websocket', 'polling', 'flashsocket']});
},[])
useEffect(()=>{
socket.on('recieve_message', (data)=>{
setMessageList([...messageList,data])
})
});
// useEffect(()=>{
// const objDiv = document.getElementById('scroll');
// objDiv.scrollTop = objDiv.scrollHeight;
// },[message])
function connectRoom(){
socket.emit('join', room)
setLogged(true);
}
function sendMessage(e){
let messageContent = {
room:room,
content:{
author:name,
message:message
}
}
socket.emit('send_message',messageContent)
setMessageList([...messageList,messageContent.content])
setMessage("");
}
return (
<div className="App">
{!logged?(
<div className="background">
<div className="box">
<span className="text-center">login</span>
<div className="input-container">
<input type="text" required="" onChange={e=> setName(e.target.value)}/>
<label>Full Name</label>
</div>
<div className="input-container">
<input type="text" required="" onChange={ e=> setRoom(e.target.value)}/>
<label>Room</label>
</div>
<button type="button" className="btn" onClick={connectRoom}>submit</button>
</div>
</div>
):(
<>
<div className="chatContainer">
<div className="messages" id="scroll">
{messageList.map((val,key)=>{
return (
<div className="messageContainer" id={val.author == name ? "You" : "Other"}>
<div className="messageIndividual">
{val.message}
</div>
<h1> {val.author}</h1>
</div>
);
})}
</div>
<div className="messageInputs">
<input type="text" placeholder="Type Here" onChange={ e=> setMessage(e.target.value)}/>
<button type="submit" onClick={sendMessage}> Send</button>
</div>
</div>
</>
)}
</div>
);
}
export default App;
here is my css file:这是我的 css 文件:
body{
margin: 0px;
padding: 0px;
}
.App{
margin: 0px;
padding: 0px;
display: grid;
place-items: center;
height: 100vh;
background-color: #2a2730;
}
.background{
margin: 0px;
padding: 0px;
width: 100%;
height: 100vh;
background-color: #e74c3c;
}
.text-center{
color:#fff;
text-transform:uppercase;
font-size: 23px;
margin: -50px 0 80px 0;
display: block;
text-align: center;
}
.box{
position:absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%);
background-color: rgba(0, 0, 0, 0.89);
border-radius:3px;
padding:70px 100px;
}
.input-container{
position:relative;
margin-bottom:25px;
}
.input-container label{
position:absolute;
top:0px;
left:0px;
font-size:16px;
color:#fff;
pointer-event:none;
transition: all 0.5s ease-in-out;
}
.input-container input{
border:0;
border-bottom:1px solid #555;
background:transparent;
width:100%;
padding:8px 0 5px 0;
font-size:16px;
color:#fff;
}
.input-container input:focus{
border:none;
outline:none;
border-bottom:1px solid #e74c3c;
}
.btn{
color:#fff;
background-color:#e74c3c;
outline: none;
border: 0;
color: #fff;
padding:10px 20px;
text-transform:uppercase;
margin-top:50px;
border-radius:2px;
cursor:pointer;
position:relative;
}
/*.btn:after{
content:"";
position:absolute;
background:rgba(0,0,0,0.50);
top:0;
right:0;
width:100%;
height:100%;
}*/
.input-container input:focus ~ label,
.input-container input:valid ~ label{
top:-12px;
font-size:12px;
}
.chatContainer {
width: 600px;
height: 350px;
border: 5px solid #0091ff;
border-radius: 10px;
display: flex;
flex-direction: column;
}
.chatContainer .messages {
flex: 80%;
width: 100%;
padding-left: 20px;
overflow-y: scroll;
/* flex-direction: column-reverse; */
}
.chatContainer .messageInputs {
flex: 20%;
width: 100%;
display: flex;
flex-direction: row;
}
.chatContainer .messageInputs input{
flex: 80%;
height: calc(100% -5px);
border: none;
border-top: 5px solid #0091ff;
padding-left: 20px;
font-size: 20px;
}
.chatContainer .messageInputs button{
flex: 20%;
height: 100%;
background-color: #0091ff;
border: none;
color: white;
font-size: 18px;
}
.messageContainer {
display: flex;
flex-direction: column;
width: calc(100% - 30px);
height: auto;
}
.messageContainer h1{
color: white;
font-family: Arial, Helvetica, sans-serif;
font-weight: 300;
font-size: 17px;
}
#You{
align-items: flex-end;
}
#Other {
align-items: flex-start;
}
#You .messageIndividual {
background-color: #5ff064;
color: black;
}
.messageIndividual {
width: 200px;
height: auto;
border-radius: 10px;
display: grid;
place-items: center;
background-color: #0091ff;
opacity: 0.9;
color: white;
font-family: Arial, Helvetica, sans-serif;
margin-right: 10px;
margin-top: 20px;
word-break: break-all;
white-space: pre-wrap;
padding: 4px;
}
You can add a span with ref bellow messages list that will be scroll into it when new messages are received.您可以添加一个带有参考波纹管消息列表的跨度,当收到新消息时将滚动到其中。
Try this example:试试这个例子:
useEffect:使用效果:
useEffect(() => {
scrollSpan.current.scrollIntoView({ behavior: "smooth" });
}, [messageList]);
useRef:使用参考:
const scrollSpan= useRef();
JSX: JSX:
<div className="messages" id="scroll">
{messageList.map((val,key)=>{
return (
<div className="messageContainer" id={val.author == name ?
"You" : "Other"}>
<div className="messageIndividual">
{val.message}
</div>
<h1> {val.author}</h1>
</div>
);
})}
<span ref={scrollSpan}></span>
</div>
On your 'messageContainer' div, create a reference using the 'useRef' React hook.在你的 'messageContainer' div 上,使用 'useRef' React 钩子创建一个引用。
...
const container = useRef();
...
..
return (
...
<div class="messageContainer" ref={ container }>...</div>
);
and use one more useEffect as follows:并使用另一种 useEffect 如下:
useEffect(() => {
container.current.scrollTop = chatBoxRef.current.scrollHeight;
}, [messageList]);
Some improvements(,important) to your code: which could prevent memory leaks in your app:您的代码的一些改进(重要):这可以防止您的应用程序中的 memory 泄漏:
-so you're doing this: - 所以你这样做:
useEffect(()=>{
socket.on('recieve_message', (data)=>{
setMessageList([...messageList,data])
})
});
This causes a new 'receive message' event to get registered to the socket every time your component re-renders.这会导致每次您的组件重新渲染时都会向套接字注册一个新的“接收消息”事件。 You don't want that.
你不想要那个。 You want this event to get registered just once.
您希望此事件只注册一次。 So do as follows:
因此,请执行以下操作:
useEffect(()=>{
socket.on('recieve_message', (data)=>{
setMessageList(prevMessageList => [...prevMessageList, data]);
})
}, []);
Note the change in setMessageList.(Look up why I did that, it is related to closures since this useEffect now runs only on the first render).注意 setMessageList 的变化。(看看我为什么这样做,它与闭包有关,因为这个 useEffect 现在只在第一次渲染时运行)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.