Почему переменные состояния были сброшены в подписке Moralis в React
Я использую React JS (с chakra-ui и @ chatscope / chat-ui-kit-react) с Moralis Web3, создавая приложение чата, в котором я хочу:
- Получить все сообщения, когда компонент загружается в первый раз, и
- Подпишитесь на сообщения всякий раз, когда в таблицу добавляется новое сообщение. Это использует веб-сокет, предоставленный Moralis.
Я могу получить первый шаг, на котором я вызываю запрос, когда компонент загружается в первый раз, используя
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 (в subscription.on) и Messages.js: 102 (in), показывающие разные результаты.
1 ответ
Я исправил это. Поэтому, когда мы хотим обновить переменную состояния массива, вместо того, чтобы пытаться скопировать переменную и использовать array.push(), мы должны использовать функцию setState, чтобы получить последнее состояние, и использовать оператор распространения, чтобы добавить новый объект в массив.
Пример Это переменная состояния, textMsgs заполнен массивом объектов, например ['яблоко', 'банан', 'лимон']
const [textMsgs, setTextMsgs] = useState([]);
Когда мы хотим добавить еще один объект, вместо того, чтобы делать это первым способом
var messages = textMsgs;
messages.push('mango');
setTextMsgs(messages);
мы должны сделать второй способ
var message = 'mango'
setTextMsgs(oldarr => [...oldarr, message])
Потому что, во-первых, каким-то образом, когда я извлекаю данные из textMsgs, они пусты. Я не уверен, почему это произошло, но если у вас есть эта проблема, вы хотите попробовать второй способ.