第一次正确获取数据,当用户移动彼此聊天时,状态值未刷新,显示过去+当前用户聊天

Posted

技术标签:

【中文标题】第一次正确获取数据,当用户移动彼此聊天时,状态值未刷新,显示过去+当前用户聊天【英文标题】:First time data is getting fetched properly,when user move to chat with one another,state value is not getting flush, shows past+current user chat 【发布时间】:2021-06-11 07:51:13 【问题描述】:

我是 react.js 和 node.js 的新手,我正在尝试构建聊天应用程序,我也使用过 socket.io。其中,当任何用户点击用户聊天时,它会调用 chat.js 组件文件,第一次显示聊天消息的正确获取结果,当用户点击与另一个人交谈时,我正在过去用户聊天 + 当前用户聊天。 我检查了后端节点服务器的数据是否正确,react.js 有问题

dashboard.js

import React,  useEffect, useState  from "react"
import  Container,Row,Col,Button from 'react-bootstrap'
import fetchUsers from '../services/services'
import useHistory from 'react-router-dom'
import Chat from "./Chat"
function Dashboard() 
    const [users,setUsers]=useState([])
    const senderId = localStorage.getItem("senderId");
    const history=useHistory()
    const setLogout=()=>
        localStorage.setItem("loggedin", false);
        history.push('/signin');
    

    /*
    fetchUsers function will be called for fetching all users, except the current logged-in user.
    */
   
    useEffect(()=>
        fetchUsers(senderId).then(res=> setUsers(res.data.message))
    )

    var receiverId;
    function handleInput(id)
        receiverId=id
        console.log(receiverId)
        localStorage.setItem("receiverId",id)
    
    
    return (
        <div>
            <Container fluid>
                <Row className="p-2">
                    <Col>
                    <h1 align="left">Login successful</h1> 
                    </Col>
                    <Col md=span:2,offset:4>
                        <Button onClick=setLogout className="btn btn-danger btn-block">Logout</Button>
                    </Col>
                </Row>
                <Row className="p-4">
                    <Col md=3>
                        <h3>
                            List of Users
                        </h3>
                        
                            users.map(
                                e=>
                                    return <Row className="pt-2 pb-2" key=e._id onClick=()=>handleInput(e._id)>
                                                <Col>
                                                    <img className="ui avatar image" alt=e.username src="https://ui-avatars.com/api/?name="+`$e.username`/>
                                                    <span>e.username</span>
                                                </Col>
                                           </Row>
                                    
                            )
                        
                    </Col>
                    <Col md=9>
                        receiverId=="" && <Chat ></Chat>
                    </Col>
                </Row>
            </Container>
        </div>
    )


export default Dashboard

聊天.js

import React,  useEffect, useRef, useState  from "react"
import io from "socket.io-client"
import  Row,Col,Form from 'react-bootstrap'
import useHistory from 'react-router-dom'
import fetchUserById,fetchRoom,fetchChats from '../services/services'
export default function Chat() 
    const history=useHistory()

    const [username,senderId,receiverId] = [localStorage.getItem("username"),localStorage.getItem("senderId"),localStorage.getItem("receiverId")];

    console.log(receiverId)

    const socketRef = useRef();

    const [chat,setChat]=useState([]);
    const [ oldChat, setOldChat ] = useState([]);
    const [receiver, setReceiver] = useState("");

    useEffect(()=>
        /*
        fetchUserById is called to find receiver id when user click on username.
        */
        fetchUserById(receiverId).then(res=> setReceiver(res.data.message[0].username))
    ,[receiverId])

    // once we get result from fetchUserById it will set the receiver name into receiver state, and here we assign it to receiverUser
    const receiverUser=receiver

    const [ state, setState ] = useState( senderId:senderId ,message: "", username: username,receiverId:receiverId,roomId:"",receiverUser:receiverUser)
    const roomId1=senderId.substr(12);
    const roomId2=receiverId.substr(12);
    /*
    fetchRoom will find the room based on current user's last 12 characters, and receiver user's last 12 characters
    where roomId1 and roomId2 both will passed into fetchRoom, 
    where roomId1, is current active user + receiver user's IDs of last 12-12 characters, total 24 characters
    same as above, roomId2 have reverse of roomId1, here receiver's + current active user 12-12 characters, total 24 characters.
    it will pass to fetchRoom and check if any chat found within this room, it will return the roomId whatever we have in database,
    else not found then it will return roomId1 as per the current user. 
    */
    useEffect(
        () => 
            fetchRoom(roomId1,roomId2).then(res=> 
                localStorage.setItem("roomId",res.data.message);
                setState(senderId,message:"",username,receiverId,roomId:res.data.message,receiverUser)
            )
            let roomId=localStorage.getItem("roomId")
            
            socketRef.current = io.connect("http://localhost:4000" ,transports: ['polling'])
            socketRef.current.on("message", (senderId,username, message,receiverId,roomId,receiverUser ) => 
                setOldChat([ ...oldChat,  senderId,username, message,receiverId,roomId,receiverUser  ])
            )
            return () => socketRef.current.disconnect()
        ,[oldChat]
    )
    // saving roomId into localStorage.
    const roomId=localStorage.getItem("roomId")
    useEffect(() => 
        fetchChats(roomId,senderId,receiverId).then(res=>
         if(res.data.status===200)
            setOldChat([...oldChat,res.data.message])
            // console.log(oldChat)
          else
             setOldChat([]);
            console.log("inside else")
             setOldChat([...oldChat,res.data.message])
            // console.log(res.data.message)
         
        )
     , [roomId, senderId, receiverId]);

    // onTextChange is helps to change value of message textbox.
    const onTextChange = (e) => 
        setState( ...state, [e.target.name]: e.target.value )
    

    /*
    on click onMessageSubmit, it will take state value, and check message is empty then it will give alert,
    else it will emit message event.
    */
    const onMessageSubmit = (e) => 
        const  senderId, message,receiverId,roomId,receiverUser  = state
        
        if(message==="")
            e.preventDefault()
            alert("please enter message")
        else
            socketRef.current.emit("message", 
                senderId,username,message,receiverId,roomId, receiverUser
            )
            setChat([...chat,senderId,username,message,receiverId,roomId,receiverUser])
            e.preventDefault()
        
    
    
    /*
    renderChat method will take oldChat state values
    we had used ternary operator for rendering values
    but, if senderId is equal to current userId then it will print with sender/username,else it will print message of receiver. 
    */
    const renderChat = () => 
        console.log("renderChat",oldChat.length)
        return oldChat.length && oldChat.map((e)=>
            return e.length ? e.map((ee)=>
                if(ee.senderId===senderId)
                    return  <Row key=ee._id>
                                <div style=display:"flex",flexDirection:"row" className="p-1">
                                        <img className="ui avatar image" alt=username src="https://ui-avatars.com/api/?name="+`$username`/><b>username</b>:ee.message
                                </div>
                            </Row>
                
                else
                    return  <Row key=ee._id>
                                <div style=marginLeft:"auto",marginRight:0 className="p-1">
                                    <div style=display:"flex",flexDirection:"row-reverse",justifyContent: "flex-end">
                                        <img className="ui avatar image" alt=receiverUser src="https://ui-avatars.com/api/?name="+`$receiverUser`/><b>receiverUser</b>ee.message:
                                    </div>
                                </div>
                            </Row>
                
            ):""
        )
    

    /*
    renderCurrentChat works as similar to renderChat as you can check above fucntion,
    it takes the current chat of user, if senderId is equal to current userId then it will print with sender/username,
    else it will print message of receiver.
    */
    const renderCurrentChat = () => 
        console.log("renderCurrentchat",chat.length)
        return chat.length ? chat.map((e)=>
            console.log(e)
                if(e.senderId===senderId)
                    return  <Row key=Math.floor(Math.random() * 999) >
                                <div style=display:"flex",flexDirection:"row" className="p-1">
                                        <img className="ui avatar image" alt=username src="https://ui-avatars.com/api/?name="+`$username`/><b>username</b>:e.message
                                </div>
                            </Row>
                
                else
                    return  <Row key=Math.floor(Math.random() * 999)>
                                <div style=marginLeft:"auto",marginRight:0 className="p-1">
                                    <div style=display:"flex",flexDirection:"row-reverse",justifyContent: "flex-end">
                                        <b>receiverUser</b><img className="ui avatar image" alt=receiverUser src="https://ui-avatars.com/api/?name="+`$receiverUser`/>e.message:
                                    </div>
                                </div>
                            </Row>
                
            
        ):""
    
    return (
            <Row className="">
                /* 
                form having senderId, username, roomId and message
                in senderId current active users Id will be displayed in disabled textbox,
                where on username it shows current active username in disabled textbox,
                in roomId it will get based on  fetchRoom function which I had discribed above,
                if user having chat then it will receive roomId, and display here in textbox with disabled,
                in message it having default state value "" empty and it will be change when it based onTextChange event
                and at last button, on submit button onMessageSubmit will be fired/called.
                */
                <Col md=8>
                    <div>
                        /* chat heading to acknowledge who talking to whom */
                        <h1>Chat log history</h1>
                        <p> username is talking to receiverUser</p>
                        <div style=maxHeight: "495px",overflow: "auto"> 
                            /* prints all message having in oldChat State */
                            <div className="pt-0 pl-5 pr-5 pb-0">
                                renderChat()
                            </div>
                            /* prints all message having in chat State */
                            <div className="pt-0 pl-5 pr-5 pb-0">
                                renderCurrentChat()
                            </div>
                        </div>
                    </div>
                </Col>
                <Col md=4>
                    <form onSubmit=onMessageSubmit>
                        <h1>Send message</h1>
                        <div hidden>
                            <input type ="text" name="senderId" disabled  value=state.senderId label="SenderId" />
                        </div>
                        <div className="p-1">
                            <Form.Control type="text" name="username" placeholder=state.username value=state.username readOnly/>
                        </div>
                        <div hidden>
                            <input type ="text" name="roomId" disabled  value=state.roomId label="RoomId" />
                        </div>
                        <div className="p-1">
                        <Form.Control type="text" name="message" placeholder="enter your message....." onChange=(e) => onTextChange(e) value=state.message id="outlined-multiline-static" variant="outlined" label="Message"/>
                        </div>
                        <button className="btn btn-primary p-2 m-2">Send Message</button>
                    </form>
                </Col>
                
                                    
            </Row>
    )

【问题讨论】:

【参考方案1】:

从 fetchChats() 中删除“...oldChat”,当数据从服务器获取您时,请参阅我从您的代码中给出的示例。

fetchChats(roomId,senderId,receiverId).then(res=>
         if(res.data.status===200)
            setOldChat([...oldChat,res.data.message])
            // console.log(oldChat)
          else
             setOldChat([]);
            console.log("inside else")
             setOldChat([...oldChat,res.data.message])
            // console.log(res.data.message)
         
        )

【讨论】:

以上是关于第一次正确获取数据,当用户移动彼此聊天时,状态值未刷新,显示过去+当前用户聊天的主要内容,如果未能解决你的问题,请参考以下文章

从聊天中获取待处理消息

用户在屏幕聊天时如何取消本地推送通知?

在黑莓上验证 Facebook 聊天时遇到问题

React 状态数据未在第一个组件渲染时显示

在反应中切换聊天时如何订阅新的websocket会话

使用firebase聊天时如何从下到上设置数据