I have the following code of which is a part of a simple React Flask socketio chat application:
const ip=window.location.hostname
const socket = io("http://" ip ":5000");
export default function Homepage()
{
const currUser=useSelector(state=>state.authReducer).loggedInUser
const [chats, setChats] = useState([])
const [selectedChatID, setSelectedChatID] = useState("")
const [selectedChatIndex, setSelectedChatIndex] = useState(-1)
const getChatsDataFromDB = async() =>
{
let chats = await messagesSVR.getMessages()
setChats(chats.data.data)
}
const handleReceiveMessage=(message)=>
{
let newMessageChatIndex = [...chats].findIndex(chat =>
{
return chat["chat_id"] == message["chatID"]})
const updatedChats = Object.assign([], chats, {
[newMessageChatIndex]: {
chat_id: selectedChatID,
chat_messages: [...chats[newMessageChatIndex].chat_messages,
message],
},
})
setChats(updatedChats)
}
useEffect(()=>
{
socket.on("receive-message",(message)=>
{
handleReceiveMessage(message)
})
socket.emit("add-user",{user_email:currUser["_id"]});
return ()=>
{
socket.off('receive-message');
}
},[])
useEffect(() => {
getChatsDataFromDB()
}, [])
useEffect(() => {
let currChatIndex = chats.findIndex(chat => chat["chat_id"] == selectedChatID)
setSelectedChatIndex(currChatIndex)
}, [selectedChatID, chats])
const handleSendNewMessage = async (messageText) => {
let message_id=v4()
let newMessage=
{
_id:message_id,
to:chats[selectedChatIndex]["partner_id"],
chatID:selectedChatID,
userID:currUser["_id"],
text:messageText,
sentAt:new Date()
}
try {
//Adding message to server
let resp = await messagesSVR.addMessage(newMessage)
//Executed if message added properly
if (resp.status == 200) {
const updatedChats = Object.assign([], chats, {
[selectedChatIndex]: {
chat_id: selectedChatID,
partner_id:[...chats][selectedChatIndex].partner_id,
chat_messages: [...chats[selectedChatIndex].chat_messages,
newMessage],
},
})
setChats(updatedChats)
socket.emit("send-message",newMessage)
}
} catch (err) {
console.log(err)
}
}
When I run the app with two tabs opened in the browser, and send a message from one tab ( tab number 1 for the sake of argument) to tab number 2 using the send-message event,the message is indeed received in tab number 2 through the receive-message event in handleReceiveMessage function, but for some reason the chats state variable value is an empty array ( I can see it when I print console.log(chats)) , though it was previously loaded with chats data from data base.
Is there any way to fix this?
CodePudding user response:
useEffect seems to be refering to the initial values through handleReceiveMessage
const handleReceiveMessage = (message) =>
{
let newMessageChatIndex = [...chats].findIndex(chat =>
{
return chat["chat_id"] == message["chatID"]})
const updatedChats = Object.assign([], chats, {
[newMessageChatIndex]: {
chat_id: selectedChatID,
chat_messages: [...chats[newMessageChatIndex].chat_messages,
message],
},
})
setChats(updatedChats)
}
const handleReceiveMessageRef = useRef();
//refer to latest version
handleReceiveMessageRef.current = handleReceiveMessage
const currUserRef = useRef();
currUserRef.current = currUser;
useEffect(()=>
{
socket.on("receive-message",(message)=>
{
handleReceiveMessageRef.current(message)
})
socket.emit("add-user",{user_email:currUserRef.current["_id"]});
return ()=>
{
socket.off('receive-message');
}
},[])
The problem you are facing is related to referring the latest props in useEffect and why it happens, it will give you an idea on how to handle such problems
Hope it helps