为什么状态变量在 React 中的 Moralis 订阅上被重置

Why state variables got reset on Moralis subscription in React

我正在使用 React JS(带有 chakra-ui 和 @chatscope/chat-ui-kit-react)和 Moralis Web3 创建一个我想要的聊天应用程序:

  1. 获取第一次加载组件时的所有消息,并且
  2. 每当 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 获取数据时,它是空的。我不确定为什么会这样,但如果你们有这个问题,你想尝试第二种方法。