聊天应用程序如何知道用户何时发送消息以重新呈现?

Posted

技术标签:

【中文标题】聊天应用程序如何知道用户何时发送消息以重新呈现?【英文标题】:How do chat apps know when a user sends a message to re render? 【发布时间】:2021-06-27 01:30:47 【问题描述】:

我需要使用 websockets 吗?我正在使用 mysql 创建一个反应应用程序,我希望它基本上是一个聊天应用程序。我可以让它在我正在使用的浏览器中重新呈现,但是如果我打开另一个选项卡并输入一条消息,我的应用程序将不会重新呈现。有什么简单的方法可以解决这个问题还是我应该使用 websockets?这是我的代码。

app.js

 useEffect(() => 
    console.log('use effect is in use.');
    fetch(`http://localhost:4000/getallmessages`)
      .then((data) => data.json())
      .then((messages) => 
        setGetAllMessages(messages);
      )
      .catch((err) => 
        console.log(err);
      );
  , []);

  return (
    <div className=classes.AppContainer>
      loggedIn ? (
        <>
          loginMessage ? setIntervalMessage() : null
          loginMessage ? (
            <h3 className=classes.SuccessMessage>
              loginMessage.successMessage
            </h3>
          ) : null

          <Chatroom
            user=user
            getAllMessages=getAllMessages
            setGetAllMessages=setGetAllMessages
          />
        </>
      ) : (
        <>
          <Login
            setSignInPassword=setSignInPassword
            setSignInUsername=setSignInUsername
            login=login
            signInPassword=signInPassword
            signInUsername=signInUsername
          />

          loginErrorMessage ? (
            <p className=classes.ErrorMessage>
              loginErrorMessage.errorMessage
            </p>
          ) : null

          <Signup
            setUsername=setUsername
            setPassword=setPassword
            createUsername=createUsername
            username=username
            password=password
          />
          signup ? <p>signup.message</p> : null
          signupError ? <p>signupError.errors[0].msg</p> : <p></p>
        </>
      )
    </div>
  );
;

这里是聊天室组件

const Chatroom = ( user, getAllMessages, setGetAllMessages ) => 
  const [getMessages, setGetMessages] = useState();
  const [userMessage, setUserMessage] = useState('');
  const [getAllUsers, setGetAllUsers] = useState();
  const [flag, setFlag] = useState(false);
  const [randomColor, setRandomColor] = useState(
    Math.floor(Math.random() * 16777215).toString(16)
  );

  const messagesEndRef = useRef(null);

  const scrollToBottom = () => 
    messagesEndRef.current?.scrollIntoView( behavior: 'smooth' );
  ;

  useEffect(() => 
    fetch(`http://localhost:4000/getallusers`)
      .then((data) => data.json())
      .then((users) => 
        console.log('-----ALL USERS------', users);
        setGetAllUsers(users);
      )
      .catch((err) => console.log(err));
  , []);

  useEffect(() => 
    fetch(`http://localhost:4000/getuser/$user.username/$user.id`)
      .then((response) => response.json())
      .then((data) => 
        setGetMessages(data.message);
      )
      .catch((err) => 
        console.log(err);
      );
  , []);

  const sendMessage = (e) => 
    setUserMessage('');

    e.preventDefault();

    const body = 
      userMessage: userMessage,
      user: user.username,
      userId: user.id,
    ;

    fetch(`http://localhost:4000/sendmessage/$user.id`, 
      method: 'POST',
      headers: 
        'Content-Type': 'application/json',
      ,
      body: JSON.stringify(body),
    )
      .then((data) => 
        return data.json();
      )
      .then((message) => 
        console.log('success', message);
        setFlag(true);
      )
      .catch((err) => 
        console.log(err);
      );
  ;

  const getUserInput = (e) => 
    setUserMessage(e.target.value);
  ;

  useEffect(() => 
    if (flag === true);
    fetch(`http://localhost:4000/getallmessages`)
      .then((data) => data.json())
      .then((messages) => 
        setGetAllMessages(messages);
        setFlag(false);
      )
      .catch((err) => 
        console.log(err);
      );
  , [flag]);

  useEffect(() => 
    scrollToBottom();
  , [getAllMessages, flag]);

  if (
    user === undefined ||
    getMessages === undefined ||
    getAllMessages === undefined ||
    getAllUsers === undefined
  )
    return <p>loading..</p>;

  return (
    <div className=classes.ChatroomContainer>
      <h1>Welcome user ? user.username : 'Elita'</h1>
      <div className=classes.ChatroomBox>
        getAllMessages.message.map((allMessages) => (
          <div key=allMessages.id>
            <p>
              getAllUsers.messages.map((user) =>
                user.id === allMessages.userId ? (
                  <span
                    key=user.id
                    className=classes.UsernameColor
                    //create a random color to differenciate users.
                    style=
                      color: '#' + randomColor,
                    
                  >
                    user.username :
                  </span>
                ) : (
                  ''
                )
              )
              allMessages.message
            </p>
            <div ref=messagesEndRef />
          </div>
        ))

        <input type='hidden' name=user ? user.username : '' />
        <input type='hidden' name=user ? user.id : '' />
      </div>
      <div className=classes.SendMessageUI>
        <input
          type='text'
          name='userMessage'
          placeholder='hi eli ;)'
          onChange=(e) => getUserInput(e)
          onKeyDownCapture=(e) => (e.keyCode === 13 ? sendMessage(e) : null)
          value=userMessage
        ></input>
        <button onClick=sendMessage>Send</button>
      </div>
    </div>
  );
;

最后用 node.js 做后端

路线

router.get('/getallusers', chatroomController.getAllUsers);
router.get('/getallmessages', chatroomController.getAllMesssages);
router.get('/getuser/:user/:userid', chatroomController.getUser);

router.post(
  '/createuser',
  body('username')
    .isLength( min: 3 )
    .withMessage('oops username must be at least 3 characters in length'),
  body('password')
    .isLength( min: 5 )
    .withMessage('oops password must have 5 characters'),
  authController.createUser
);

router.post(
  '/signin',
  body('signInUsername')
    .isLength( min: 3 )
    .withMessage('oops username needs at least 5 characters'),
  body('signInPassword')
    .isLength( min: 5 )
    .withMessage('oops password needs to be at least 5 characters'),
  authController.postSignin
);

router.post('/sendmessage/:userid', chatroomController.postSendMessage);

和控制器

exports.getAllUsers = async (req, res, next) => 
  User.findAll()
    .then((user) => 
      if (!user) 
        res.json( message: 'oops no users' );
        return next();
      
      res.json( messages: user );
      console.log(user);
    )
    .catch((err) => 
      console.log(err);
    );
;

exports.getAllMesssages = (req, res, next) => 
  Message.findAll()
    .then((messages) => 
      if (!messages) 
        console.log('oops no messages');
        res.json( message: 'oops no messages' );
        return next();
      
      console.log(messages);
      res.json( message: messages );
    )
    .catch((err) => 
      console.log(err);
    );
;

exports.getUser = (req, res, next) => 
  Message.findAll( where:  userId: req.params.userid  )
    .then((userAndMessage) => 
      if (!userAndMessage) 
        res.json( message: 'oops something went wrong.' );
        return next();
      
      console.log(userAndMessage);
      res.json( message: userAndMessage, user: userAndMessage );
    )
    .catch((err) => 
      console.log(err);
      return err;
    );
;

exports.postSendMessage = (req, res, next) => 
  const message = req.body.userMessage;
  const userId = req.params.userid;
  console.log(req.params);
  Message.create(
    message: message,
    userId: userId,
  )
    .then((message) => 
      console.log(message);
      res.json( message: 'success!', message: message );
    )
    .catch((err) => 
      console.log(err);
      res.json( message: 'something went wrong.' );
    );
;

【问题讨论】:

【参考方案1】:

我建议使用 Web 套接字或 MQTT,使用 MQTT,您需要连接到像 Mosquitto 或 CloudMQTT 这样的代理才能订阅主题。然后,您可以在 React 应用程序中实现逻辑以在消息到达时更新状态,这将导致页面重新呈现。如果您采用这种方法并设置客户端,我会选择Paho。

这里有一个sn-p给你一个想法:

const [message, setMessage] = useState("");

const client = new Paho.MQTT.Client(location.hostname, Number(location.port), "clientId");

// set callback handlers
client.onMessageArrived = onMessageArrived;
 
// called when a message arrives
const onMessageArrived = (message) => 
    setMessage(message.payloadstring);

【讨论】:

以上是关于聊天应用程序如何知道用户何时发送消息以重新呈现?的主要内容,如果未能解决你的问题,请参考以下文章

phpcms如何将内容发送到微信号

js节点-socket.io聊天修改

关闭了5G消息,还能使用聊天机器人(Chatbot)吗?给聊天机器人(Chatbot)发短信是不是会

JAVA开发微信小程序客服,如何让客服使用手机接收用户消息啊?

每秒更新来自 api 的消息

如何将每个用户的最后一条消息显示给用户对话以保留聊天记录?