为什么状态变量在 React 中的 Moralis 订阅上被重置
Why state variables got reset on Moralis subscription in React
我正在使用 React JS(带有 chakra-ui 和 @chatscope/chat-ui-kit-react)和 Moralis Web3 创建一个我想要的聊天应用程序:
- 获取第一次加载组件时的所有消息,并且
- 每当 table 中添加新消息时订阅消息。这是使用 Moralis 提供的 websocket。
我能够在使用 useEffect
第一次加载组件时调用查询的第一步。我将它存储在状态变量 textMsgs
.
中
问题是当我订阅时,在
subscription.on("create", (object) => {
console.log(textMsgs);
});
console.log 结果总是空的。而它应该显示我最初获取的最后一个数组。
然后我加一个
<Button onClick={() => console.log(textMsgs)}>get textMsgs</Button>
检查 textMsgs 是否可以显示数组,它确实显示了数组。
我很困惑为什么在subscription.on中,textMsgs是空的,但是当我点击按钮时,为什么在加载组件时显示第一个提取?
完整代码:Messages.js
import { Container, Button, Text } from "@chakra-ui/react";
import Moralis from "moralis/dist/moralis.min.js";
import styles from "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
import {
MainContainer,
ChatContainer,
MessageList,
Message,
MessageInput,
} from "@chatscope/chat-ui-kit-react";
import { useState, useEffect } from "react";
export const Messages = ({ roomId, userId }) => {
const [textMessage, setTextMessage] = useState("");
const [textMsgs, setTextMsgs] = useState("");
useEffect(() => {
getMessages();
subscribeMessages();
}, []);
const subscribeMessages = async () => {
const query = new Moralis.Query("Messages");
query.equalTo("roomId", roomId);
const subscription = await query.subscribe();
// on subscription object created
subscription.on("create", (object) => {
console.log(textMsgs);
});
};
// Get textMsgs in this room
const getMessages = async () => {
const Message = Moralis.Object.extend("Messages");
const message = new Moralis.Query(Message);
message.equalTo("roomId", roomId);
const msgResults = await message.find();
var msgs = [];
for (let i = 0; i < msgResults.length; i++) {
var username = await getUsername(msgResults[i].attributes.userId);
var msg = {
msgId: msgResults[i].id,
createdAt: msgResults[i].attributes.createdAt,
userId: msgResults[i].attributes.userId,
textMessage: msgResults[i].attributes.textMessage,
username: username,
};
msgs.push(msg);
}
setTextMsgs(msgs);
};
const getUsername = async (userId) => {
// Query username
const User = Moralis.Object.extend("User");
const user = new Moralis.Query(User);
user.equalTo("objectId", userId);
const userResults = await user.find();
return userResults[0].attributes.username;
};
const sendMessage = (e) => {
var newMsg = {
textMessage: textMessage,
userId: userId,
roomId: roomId,
};
const Message = Moralis.Object.extend("Messages");
const message = new Message();
message.set(newMsg);
message.save().then(
(msg) => {
// Execute any logic that should take place after the object is saved.
//alert("New object created with objectId: " + msg.id);
},
(error) => {
// Execute any logic that should take place if the save fails.
// error is a Moralis.Error with an error code and message.
alert("Failed to create new object, with error code: " + error.message);
}
);
};
return (
<Container>
{/* react chat */}
<div
style={{
height: "100vh",
}}
>
<Button onClick={() => console.log(textMsgs)}>get textMsgs</Button>
<MainContainer style={{ border: "0" }}>
<ChatContainer>
<MessageList>
<MessageList.Content>
{textMsgs &&
textMsgs.map((data, key) => {
return (
<div key={"0." + key}>
<Text
key={"1." + key}
align={data.userId === userId ? "right" : "left"}
fontSize="xs"
>
{data.username}
</Text>
<Message
model={{
message: data.textMessage,
direction:
data.userId === userId ? "outgoing" : "incoming",
sentTime: "just now",
sender: "Joe",
}}
key={"2." + key}
/>
</div>
);
})}
</MessageList.Content>
</MessageList>
<MessageInput
value={textMessage}
placeholder="Type message here"
attachButton={false}
onChange={(text) => {
setTextMessage(text);
}}
onSend={sendMessage}
/>
</ChatContainer>
</MainContainer>
</div>
</Container>
);
};
在这里你可以看到 Messages.js:30 (in subscription.on) 和 Messages.js:102 (in ) 显示不同的结果
我解决了这个问题。所以当我们想要更新一个数组状态变量时,我们必须使用 setState 函数获取最后一个状态并使用扩展运算符将新对象添加到数组中,而不是尝试复制变量并使用 array.push()数组。
例子
这是状态变量,textMsgs 填充了一个对象数组,例如 [‘apple’, ‘banana’, ‘lemon’]
const [textMsgs, setTextMsgs] = useState([]);
当我们想添加另一个对象时,而不是第一种方式
var messages = textMsgs;
messages.push('mango');
setTextMsgs(messages);
我们应该采用第二种方式
var message = 'mango'
setTextMsgs(oldarr => [...oldarr, message])
因为在第一种方式中,不知何故,当我从 textMsgs 获取数据时,它是空的。我不确定为什么会这样,但如果你们有这个问题,你想尝试第二种方法。
我正在使用 React JS(带有 chakra-ui 和 @chatscope/chat-ui-kit-react)和 Moralis Web3 创建一个我想要的聊天应用程序:
- 获取第一次加载组件时的所有消息,并且
- 每当 table 中添加新消息时订阅消息。这是使用 Moralis 提供的 websocket。
我能够在使用 useEffect
第一次加载组件时调用查询的第一步。我将它存储在状态变量 textMsgs
.
问题是当我订阅时,在
subscription.on("create", (object) => {
console.log(textMsgs);
});
console.log 结果总是空的。而它应该显示我最初获取的最后一个数组。
然后我加一个
<Button onClick={() => console.log(textMsgs)}>get textMsgs</Button>
检查 textMsgs 是否可以显示数组,它确实显示了数组。
我很困惑为什么在subscription.on中,textMsgs是空的,但是当我点击按钮时,为什么在加载组件时显示第一个提取?
完整代码:Messages.js
import { Container, Button, Text } from "@chakra-ui/react";
import Moralis from "moralis/dist/moralis.min.js";
import styles from "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
import {
MainContainer,
ChatContainer,
MessageList,
Message,
MessageInput,
} from "@chatscope/chat-ui-kit-react";
import { useState, useEffect } from "react";
export const Messages = ({ roomId, userId }) => {
const [textMessage, setTextMessage] = useState("");
const [textMsgs, setTextMsgs] = useState("");
useEffect(() => {
getMessages();
subscribeMessages();
}, []);
const subscribeMessages = async () => {
const query = new Moralis.Query("Messages");
query.equalTo("roomId", roomId);
const subscription = await query.subscribe();
// on subscription object created
subscription.on("create", (object) => {
console.log(textMsgs);
});
};
// Get textMsgs in this room
const getMessages = async () => {
const Message = Moralis.Object.extend("Messages");
const message = new Moralis.Query(Message);
message.equalTo("roomId", roomId);
const msgResults = await message.find();
var msgs = [];
for (let i = 0; i < msgResults.length; i++) {
var username = await getUsername(msgResults[i].attributes.userId);
var msg = {
msgId: msgResults[i].id,
createdAt: msgResults[i].attributes.createdAt,
userId: msgResults[i].attributes.userId,
textMessage: msgResults[i].attributes.textMessage,
username: username,
};
msgs.push(msg);
}
setTextMsgs(msgs);
};
const getUsername = async (userId) => {
// Query username
const User = Moralis.Object.extend("User");
const user = new Moralis.Query(User);
user.equalTo("objectId", userId);
const userResults = await user.find();
return userResults[0].attributes.username;
};
const sendMessage = (e) => {
var newMsg = {
textMessage: textMessage,
userId: userId,
roomId: roomId,
};
const Message = Moralis.Object.extend("Messages");
const message = new Message();
message.set(newMsg);
message.save().then(
(msg) => {
// Execute any logic that should take place after the object is saved.
//alert("New object created with objectId: " + msg.id);
},
(error) => {
// Execute any logic that should take place if the save fails.
// error is a Moralis.Error with an error code and message.
alert("Failed to create new object, with error code: " + error.message);
}
);
};
return (
<Container>
{/* react chat */}
<div
style={{
height: "100vh",
}}
>
<Button onClick={() => console.log(textMsgs)}>get textMsgs</Button>
<MainContainer style={{ border: "0" }}>
<ChatContainer>
<MessageList>
<MessageList.Content>
{textMsgs &&
textMsgs.map((data, key) => {
return (
<div key={"0." + key}>
<Text
key={"1." + key}
align={data.userId === userId ? "right" : "left"}
fontSize="xs"
>
{data.username}
</Text>
<Message
model={{
message: data.textMessage,
direction:
data.userId === userId ? "outgoing" : "incoming",
sentTime: "just now",
sender: "Joe",
}}
key={"2." + key}
/>
</div>
);
})}
</MessageList.Content>
</MessageList>
<MessageInput
value={textMessage}
placeholder="Type message here"
attachButton={false}
onChange={(text) => {
setTextMessage(text);
}}
onSend={sendMessage}
/>
</ChatContainer>
</MainContainer>
</div>
</Container>
);
};
在这里你可以看到 Messages.js:30 (in subscription.on) 和 Messages.js:102 (in ) 显示不同的结果
我解决了这个问题。所以当我们想要更新一个数组状态变量时,我们必须使用 setState 函数获取最后一个状态并使用扩展运算符将新对象添加到数组中,而不是尝试复制变量并使用 array.push()数组。
例子 这是状态变量,textMsgs 填充了一个对象数组,例如 [‘apple’, ‘banana’, ‘lemon’]
const [textMsgs, setTextMsgs] = useState([]);
当我们想添加另一个对象时,而不是第一种方式
var messages = textMsgs;
messages.push('mango');
setTextMsgs(messages);
我们应该采用第二种方式
var message = 'mango'
setTextMsgs(oldarr => [...oldarr, message])
因为在第一种方式中,不知何故,当我从 textMsgs 获取数据时,它是空的。我不确定为什么会这样,但如果你们有这个问题,你想尝试第二种方法。