Reputation: 153
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!
Upvotes: 1
Views: 3401
Reputation: 1022
I guess your value is capture, have a look the following like for more detail.
Steps to fix it:
const myFuncRef = useRef(null)
myFuncRef .current = myFunc
onClick={val => myFuncRef .current(val)}
How to get the previous props or state?
How React Hooks Components Capture Values (And How To Use the Latest Values Instead)
Upvotes: 0
Reputation: 15166
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!
Upvotes: 4