[英]Converting MERN chat app to real-time using socket.io
我一直在嘗試使用 MERN 堆棧(React-Native 而不是 React)開發實時聊天應用程序並且成功,但我無法使用socket.io
庫將其轉換為實時聊天應用程序。 下面我從我的項目中提供了一些代碼,描述了使用socket.io
之前和之后的情況/結構:
流動
用戶使用他的電話號碼注冊/登錄,在主屏幕的每次第一次渲染時,如果用戶有房間,我將獲取房間。 (房間 => 與人的對話,例如 whatsapp 的主屏幕)。 每個room
都有一個唯一的roomId
和這 2 個用戶的數據(請參閱Room
模式)。 現在當他/她點擊進入ChatScreen
時,用戶可以互相發送消息(參考Message
模式)。 為了獲取特定聊天的所有消息,我使用唯一的roomId
來獲取僅具有該roomId
的所有消息。 現在,問題是,當其他用戶發送任何消息時,我必須重新渲染整個應用程序以獲取新消息,因此,沒有實時性。
服務器
我的 mongodb 中有 3 個 collections,1) users
,2) rooms
3) messages
。
模式:
const RoomSchema = mongoose.Schema({
roomId: String,
usersId: [String],
users: Object,
}, {
timestamps: true,
});
module.exports = mongoose.model('Room', RoomSchema);
const MessageSchema = mongoose.Schema({
roomId: String,
senderId: String,
text: String,
}, {
timestamps: true,
});
module.exports = mongoose.model('Message', MessageSchema);
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
phone: {
type: String,
required: true,
trim: true,
unique: true,
},
otp: String,
}, {
timestamps: true,
});
module.exports = mongoose.model('User', UserSchema);
index.js
const express = require('express');
const mongoose = require('mongoose');
const http = require('http');
const socketio = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketio(server).sockets;
mongoose.connect(process.env.MONGO_URL);
mongoose.connection.on('connected', () => {
console.log('[INFO] Connected to MongoDB');
});
mongoose.connection.on('error', (error) => {
console.log(`[ERROR] ${error}`);
});
// SOCKETS MIDDLEWARE
require('./middlewares/socket')(io);
server.listen(process.env.PORT, () => {
console.log(`[INFO] Server running at ${process.env.PORT}`);
});
socket.js
const Message = require('../models/Message');
module.exports = (io) => {
io.on('connection', (socket) => {
console.log('A user connected.');
socket.on('sent_messages', async ({ roomId }, cb) => {
const messages = await Message.find({roomId});
cb(messages);
});
socket.on('send2user', async (data) => {
socket.broadcast.to(data.roomId).emit();
const message = new Message({
roomId: data.roomId,
senderId : data.senderId,
text: data.text,
});
const result = await message.save();
});
});
};
現在,基本上,每當用戶觸摸前端的聯系人項目時,將為這 2 個用戶(私人 1-1 聊天應用程序)創建一個房間(聊天室)。 所以,現在這 2 個用戶已經准備好進行實時聊天了。 用於獲取和創建消息的端點(雖然,我在socket.js
文件中創建了一條新消息,但不知道如何進一步處理):
router.post('/create_message', async (req, res) => {
const {roomId, senderId, text} = req.body;
try {
const message = new Message({
roomId,
senderId,
text,
});
const result = await message.save();
return res.status(200).json({
type: 'success',
data: result,
});
} catch (error) {
return res.status(422).send({error: `${error.message}`});
}
});
router.post('/get_messages', async (req, res) => {
const {roomId} = req.body;
try {
const messages = await Message.find({roomId});
return res.status(200).json({
type: 'success',
data: messages,
});
} catch (error) {
return res.status(422).send({error: `${error.message}`});
}
});
前端
實用程序.js
export const socket = io(API_URL, {forceNew: true});
socket.on('connection', () => {
console.log('Connected to server');
});
export const fetchMessages = (roomId, setMessages) => {
// socket.emit('sent_messages', {roomId}, (data) => {
// setMessages(data);
// });
AsyncStorage.getItem('token')
.then(token => {
if (token) {
fetch(`${API_URL}/message/get_messages`, {
method: 'POST',
headers: {
...
},
body: JSON.stringify({roomId}),
})
.then(response => response.json())
.then(data => {
if (data.type === 'success') {
setMessages(data.data);
}
if (data.error) {
console.log(data.error);
}
})
.catch(error => {
console.log('[ERROR] While fetching messages: ' + error.message);
});
} else {
console.log('token is null');
}
})
.catch(error => {
console.log('[ERROR] While fetching token: ' + error.message);
});
};
export const createMessage = (message, setMessages) => {
AsyncStorage.getItem('token')
.then(token => {
if (token) {
fetch(`${API_URL}/message/create_message`, {
method: 'POST',
headers: {
...
},
body: JSON.stringify(message),
})
.then(response => response.json())
.then(data => {
if (data.type === 'success') {
const latestMessage = data.data;
setMessages((prevMessages) => ([
...prevMessages,
latestMessage,
]));
// socket.emit('send2user', latestMessage);
}
if (data.error) {
console.log(data.error);
}
})
.catch(error => {
console.log('[ERROR] While fetching messages: ' + error.message);
});
} else {
console.log('token is null');
}
})
.catch(error => {
console.log('[ERROR] While fetching token: ' + error.message);
});
};
ChatScreen.js
const ChatScreen = () => {
const {params} = useRoute();
const roomId = params?.roomId;
const navigator = useNavigation();
const {user, rooms} = useAuth();
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const sendMessage = () => {
if (input) {
const message = {
roomId,
senderId: user._id,
text: input,
};
createMessage(message, setMessages);
setInput('');
}
};
useEffect(() => {
fetchMessages(roomId, setMessages);
}, []);
const scrollViewRef = useRef();
return (
<SafeAreaView>
<KeyboardAvoidingView>
<>
{/* RENDER MESSAGES WITH SCROLLVIEW */}
<ScrollView
ref={scrollViewRef}
onContentSizeChange={() =>
scrollViewRef.current.scrollToEnd({animated: true})
}
onLayout={() =>
scrollViewRef.current.scrollToEnd({animated: true})
}>
{messages.length > 0 ? (
messages.map((message, index) => (
<MessageItem key={index} myID={user._id} data={message} />
))
) : (
<Text>Start Chatting</Text>
)}
</ScrollView>
<View>
<View>
<TextInput
value={input}
onChangeText={setInput}
placeholder="Type here"
/>
</View>
<TouchableOpacity
onPress={sendMessage}>
<IonIcon name="ios-add" size={28} color="#fff" />
</TouchableOpacity>
</View>
</>
</KeyboardAvoidingView>
</SafeAreaView>
);
};
export default ChatScreen;
因此,如您所見,我無法使用socket.io
使其實時,而且我也很困惑是使用套接字還是使用 api 端點獲取/創建消息。 如果有人可以幫助我解決這個問題,我將不勝感激。 我只是想讓這項工作像使用 socketio 的實時聊天應用程序一樣工作。
在前端,您使用 API 發送消息,而不是使用 Socket on 並實時發出。 由於您使用的是 API,因此聊天只有在刷新時才會更新。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.