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

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

我是 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

chat.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>
    )
}

从 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)
         }
        })