Почему переменные состояния были сброшены в подписке Moralis в React

Я использую React JS (с chakra-ui и @ chatscope / chat-ui-kit-react) с Moralis Web3, создавая приложение чата, в котором я хочу:

  1. Получить все сообщения, когда компонент загружается в первый раз, и
  2. Подпишитесь на сообщения всякий раз, когда в таблицу добавляется новое сообщение. Это использует веб-сокет, предоставленный 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, они пусты. Я не уверен, почему это произошло, но если у вас есть эта проблема, вы хотите попробовать второй способ.

Другие вопросы по тегам