Incorrect state upon hook callback

允我心安 提交于 2020-03-05 03:01:47

问题


I'm writing a chat client using socket.io and react hooks. The message history (chatMessages) when addMessage (upon receiving the message event) is called is incorrect (always an empty array). chatMessages does update (correctly), but on the next call it is empty. There shouldn't be any other variable named chatMessages and messages is in the parent class (the set hook works so I don’t see why the state should be invalid). The state doesn't appear to update again either, so it appears to something in add messages.

import React, { useState, useCallback, useEffect } from "react";
import "./styles/chat.css";
import { uuidv4 } from "./utils/utils";
const SERVER_URL = "http://localhost:3337";
const io = require("socket.io-client");


function ChatManager(props) {
  // How to handle sockets (where do we store them)
  const [UId, setUId] = useState("");
  const [socket, setSocket] = useState(null);
  const [chatMessages, setChatMessages] = useState([]);
  const [role, setRole] = useState("Role");
  const useForceUpdate = () => useState()[1];
  function displayMessage(msg) {
    if (UId) {
      if (msg.user === UId) {
        return (
          <article class="msg-container msg-self" id="msg-0">
            <div class="msg-box">
              <div class="flr">
                <div class="messages">
                  <p class="msg" id="msg-0">
                    {msg.message}
                  </p>
                </div>
              </div>
            </div>
          </article>
        );
      } else {
        return (
          <article class="msg-container msg-remote" id="msg-0">
            <div class="msg-box">
              <div class="flr">
                <div class="messages">
                  <p class="msg" id="msg-0">
                    {msg.message}
                  </p>
                </div>
              </div>
            </div>
          </article>
        );
      }
    }
  }

  function addMessage(msg) {
    console.log("message: ");
    console.log(msg);
    const newMessages = [...chatMessages];
    console.log(chatMessages);
    newMessages.push(msg);
    console.log(newMessages);
    setChatMessages(newMessages);
    console.log("Message added");

  }

  useEffect(() => {
    console.log("did mount");
    if (!socket) {
      setUId(uuidv4());
      console.log("Set UId");
      setSocket(io.connect(SERVER_URL));
      console.log("Socket mounted");
    } else {
      console.log("Socket emit");

      socket.emit("join", {
        uid: UId,
        tid: "Task ID, add later",
        role: role
      });

      socket.on("error", function(err) {
        console.log("received socket error:");
        console.log(err);
      });

      socket.on("message", function(msg) {
     addMessage(msg);
      });
    }
    return () => {
      console.log("Unmounting");
      if (socket) {
        socket.emit("leave", {
          uid: UId,
          tid: "Task ID, add later",
          role: role
        });
        console.log("Leaving message sent");
      }
    };
  }, [socket]);

  function sendMessage() {
    const msg = "Test message";
    socket.emit("usr_input", {
      uid: UId,
      tid: "Task ID, add later",
      message: msg,
      role: role
    });

    console.log("Message sent");
  }

return (
    <div>
      <h1> Here is the chatbox </h1>
      <ChatBox 
      sendMessage={sendMessage} 
      chatMessages={chatMessages}
      displayMessage={displayMessage}/>
      <h1> EOF </h1>
    </div>
  );
}

function ChatBox(props) {
  return (
    <section class="chatbox">
      <section id="chat-messages" class="chat-window">
        {props.chatMessages.map( (msg) => {props.displayMessage(msg)})}
      </section>

    <form class="chat-input" onsubmit="return false;">
        <input
          type="text"
          id="message"
          autocomplete="on"
          placeholder="Type a message"
        />
        <button type="button" onClick={props.sendMessage}>
          <svg
            style={{
              width: "24px",
              height: "24px"
            }}
            viewBox="0 0 24 24"
          >
            {" "}
            <path
              fill="rgba(0,0,0,.38)"
              d="M17,12L12,17V14H8V10H12V7L17,12M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L5,8.09V15.91L12,19.85L19,15.91V8.09L12,4.15Z"
            />{" "}
          </svg>{" "}
        </button>
      </form>

Any ideas or workarounds would be greatly appreciated!


回答1:


What about if you mutate the previous state of your chatMessages?

As the useState hook functional updates' documentation states:

If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.

Maybe you can try as the following code snippet:

function addMessage(msg) {
  setChatMessages(previousMessages => {
    return [
      ...previousMessages,
      msg
    ];
  });
}

I hope that helps!



来源:https://stackoverflow.com/questions/59566569/incorrect-state-upon-hook-callback

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!