简体   繁体   English

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

[英]Why state variables got reset on Moralis subscription in React

I am using React JS (with chakra-ui and @chatscope/chat-ui-kit-react) with Moralis Web3 creating a chat app where I want to:我正在使用 React JS(带有 chakra-ui 和 @chatscope/chat-ui-kit-react)和 Moralis Web3 创建一个我想要的聊天应用程序:

  1. Get all the messages when component is loaded the first time, and首次加载组件时获取所有消息,并且
  2. Subscribe to messages whenever there's a new message added in the table.每当表中添加新消息时订阅消息。 This is using websocket provided by Moralis.这是使用 Moralis 提供的 websocket。

I am able to get the first step where I call a query when the component is loaded the first time using useEffect .第一次使用useEffect加载组件时,我能够获得调用查询的第一步。 I store it in a state variable textMsgs .我将它存储在 state 变量textMsgs中。

The problem is when I subscribe, in the问题是当我订阅时,在

subscription.on("create", (object) => {
      console.log(textMsgs);
});

the console.log result is always empty. console.log 结果始终为空。 Whereas it should show the last array of what I fetch initially.而它应该显示我最初获取的最后一个数组。

I then add a然后我添加一个

<Button onClick={() => console.log(textMsgs)}>get textMsgs</Button>

to check if the textMsgs can show the array, and it DOES show the array.检查 textMsgs 是否可以显示数组,并且它确实显示了数组。

I'm really confused why in subscription.on, the textMsgs is empty, but when I click the button, why is it showing the first fetched when component is loaded?我真的很困惑为什么在subscription.on 中,textMsgs 是空的,但是当我单击按钮时,为什么它在加载组件时显示第一个获取?

Full Code: Messages.js完整代码: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>
  );
};

Here you can see the Messages.js:30 (in subscription.on) and Messages.js:102 (in ) showing different result在这里您可以看到 Messages.js:30 (in subscription.on) 和 Messages.js:102 (in ) 显示不同的结果在此处输入图像描述

I fixed this.我解决了这个问题。 So when we want to update an array state variable, instead of trying to copy the variable and use array.push(), we must use the setState function to get the last state and use spread operator to add the new object in the array. So when we want to update an array state variable, instead of trying to copy the variable and use array.push(), we must use the setState function to get the last state and use spread operator to add the new object in the array.

Example This is the state variable, the textMsgs is filled with an array of objects, example ['apple', 'banana', 'lemon']示例 这是 state 变量,textMsgs 填充了对象数组,例如 ['apple', 'banana', 'lemon']

const [textMsgs, setTextMsgs] = useState([]);

When we want to add another object, instead of doing this first way当我们想添加另一个 object 时,而不是第一种方式

var messages = textMsgs;
messages.push('mango');
setTextMsgs(messages);

we should do the second way我们应该做第二种方式

var message = 'mango'
setTextMsgs(oldarr => [...oldarr, message])

Because in the first way, somehow when I'm fetching the data from textMsgs it's empty.因为在第一种方式中,当我从 textMsgs 获取数据时,它是空的。 I'm not sure why it happened, but if you guys have this issue you want to try the second way.我不确定它为什么会发生,但是如果你们有这个问题,你想尝试第二种方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM