问题
i currently have 2 screens an all messages screen which includes all the chats i participated in and a chat screen which is the actual chat itself.
In my chat screen i successfully implemented sockets, so when both users are on the chat screen at the same time messages are getting exchanged live.
My problem:
Example: if user 1 is on the allmessages screen and user 2 is inside the chat. And user 2 sends user 1 a message, user 1's screen does not automatically update with the last message for the conversation the message was sent to, I need to either scroll to refresh or navigate from one page to the other in order for it to appear.
I got suggested to use socket on the allmessages screen to listen to any event change.
In my database i have a conversations table and a messages table:
the conversation table has the following fields: id,user1,user2,postId,lastMessage
the messages table has the following fields:id, senderID,receiverID, conversationId, message
CLIENT
Chatscreen.js
useEffect(() => {
const newsocket =io.connect("IP:PORT")
setMessages(message.Messages)
newsocket.on('connect', msg => {
console.log(`user: ${user.id} has joined conversation ${message.id}`)
setSocket(newsocket)
newsocket.emit('subscribe', message.id);
});
newsocket.on("send_message", (msg) => {
console.log("this is the chat messages:", msg);
setMessages(messages => messages.concat(msg))
});
return(()=>newsocket.close());
}, []);
const onSend = (ConversationId,senderId,receiverId,message) => {
console.log("sent")
const to = (user.id===route.params.message.user1?
route.params.message.user2:route.params.message.user1)
socket.emit('message', { to: to, from: user.id, message,ConversationId });
setText("")
messagesApi.sendMessage({ConversationId,senderId,receiverId,message});
};
allmessagesScreen.js
const [posts, setPosts] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const[page,setPage]=useState(0);
const [refreshing, setRefreshing] = useState(false);
const loadPosts = async () => {
setLoading(true);
const response = await messagesApi.getMessages();
setLoading(false);
if(refreshing) setRefreshing(false);
if (!response.ok) return setError(true);
setError(false);
setPosts(response.data)
};
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
loadPosts();
});
return unsubscribe;
}, [navigation]);
return(
<FlatList
data={posts}
keyExtractor={(post) => post.id.toString()}
renderItem={({ item,index }) => (
<MessagesList
title={item.Post.title}
subTitle={item.Messages[0].message}
onPress={() => navigation.navigate(routes.CHAT,{message:item,index})}
/>
)}
refreshing={refreshing}
onRefresh={() => {
loadPosts()
}}
/>
SERVER
io.on('connection',(socket)=>{
console.log('User '+socket.id+' connected')
socket.on('subscribe', (room)=> {
console.log('joining room', room);
socket.join(room);
});
socket.on('message', (data) => {
console.log(data)
console.log('sending room post',data.ConversationId)
io.sockets.in(data.ConversationId).emit('send_message', { message:
data.message, receiverId:
data.to,senderId:data.from,conversationId:data.ConversationId });
})
})
UPDATE
i gave the following a try, my console logs are getting printed but the functionaility still doesnt work.
CLIENT
Chatscreen.js
useEffect(() => {
const newsocket =io.connect("IP:PORT")
setMessages(message.Messages)
newsocket.on('connect', msg => {
console.log(`user: ${user.id} has joined conversation ${message.id}`)
setSocket(newsocket)
newsocket.emit('subscribe', message.id);
});
newsocket.on("send_message", (msg) => {
console.log("this is the chat messages:", msg);
setMessages(messages => messages.concat(msg))
});
return(()=>newsocket.close());
}, []);
const onSend = (ConversationId,senderId,receiverId,message) => {
console.log("sent")
const to = (user.id===route.params.message.user1?
route.params.message.user2:route.params.message.user1)
socket.emit('message', { to: to, from: user.id, message,ConversationId });
setText("")
messagesApi.sendMessage({ConversationId,senderId,receiverId,message});
};
allmessagesScreen.js
const [posts, setPosts] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const[page,setPage]=useState(0);
const [refreshing, setRefreshing] = useState(false);
const loadPosts = async () => {
setLoading(true);
const response = await messagesApi.getMessages();
setLoading(false);
if(refreshing) setRefreshing(false);
if (!response.ok) return setError(true);
setError(false);
setPosts(response.data)
};
useEffect(() => {
const newsocket =io.connect("http://ip:port")
loadPosts()
newsocket.on('connect', msg => {
console.log(`waiting for user: ${user.id} to join a conversation`) //this gets printed
setSocket(newsocket)
newsocket.emit('waiting', user.id);
});
newsocket.on("send_message", (msg) => {
console.log("this is the last message:", msg); //this doesnt get printed
});
}, []);
return(
<FlatList
data={posts}
keyExtractor={(post) => post.id.toString()}
renderItem={({ item,index }) => (
<MessagesList
title={item.Post.title}
subTitle={item.Messages[0].message}
onPress={() => navigation.navigate(routes.CHAT,{message:item,index})}
/>
)}
refreshing={refreshing}
onRefresh={() => {
loadPosts()
}}
/>
SERVER
io.on('connection',(socket)=>{
console.log('User '+socket.id+' connected')//this gets printed
socket.on('subscribe', (room)=> {
console.log('joining room', room);//this gets printed
socket.join(room);
});
socket.on('message', (data) => {
console.log(data) //this gets printed
console.log('sending room post',data.ConversationId) //this gets printed
io.sockets.in(data.ConversationId).emit('send_message', { message:
data.message, receiverId:
data.to,senderId:data.from,conversationId:data.ConversationId });
})
socket.on('waiting', (user)=> {
console.log('user', user, 'is waiting.');//this gets printed
});
})
回答1:
I've just got same problem yesterday. But now it has been fixed. This one should works
import React, { useEffect, useState, useRef } from 'react'
import socket from 'socket.io-client'
const Chatbox = () => {
const [chats, setChats] = useState([])
const [message, setMessage] = useState('')
const socketClientRef = useRef()
useEffect(() => {
const client = socket("http://localhost:3002");
client.on("connect", () => {
console.log('connected')
})
client.on("disconnect", () => {
console.log('diconnected')
});
client.on("chat", message => {
setChats(prevChats => [...prevChats, message])
// INSTEAD OF:
// setChats([...chats, message])
});
socketClientRef.current = client
return () => {
client.removeAllListeners()
}
}, [])
const handleSend = async () => {
socketClientRef.current.emit('chat', {
room: `event-${eventId}`,
message
})
setMessage('')
}
return (
<div>
<div>
<h1>Messages</h1>
{chats.map(chat => (
<div>{chat}</div>
))}
</div>
<div>
<input value={message} onChange={e => setMessage(e.target.value)} />
<button onClick={handleSend}>Send</button>
</div>
</div>
)
}
回答2:
: if user 1 is on the allmessages screen and user 2 is inside the chat. And user 2 sends user 1 a message, user 1's screen does not automatically update with the last message for the conversation the message was sent to
When user2
sends a message, emit an event with socketid
of user1
. The message will be received by user1
with help the on
event. It doesn't matter on which page user1
will be as long as user1
is listening to the event.
Just make sure to send the metadata of the messages so that you will be able to differentiate and handle the messages on client side.
来源:https://stackoverflow.com/questions/65682609/socket-io-how-to-display-the-lastmessage-sent-when-user-is-not-inside-the-chat