簡體   English   中英

使用 socket.io 將 MERN 聊天應用程序轉換為實時

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM