简体   繁体   English

如何使 div 在反应中自动滚动(钩子)

[英]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;
    
  
  }
  


here is what the chat application looks like:这是聊天应用程序的样子: 在此处输入图像描述

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM