簡體   English   中英

反應 state 更新沒有 setState function - 意外

[英]React state updates without a setState function - Unexpected

我有幾個聊天組件, chat (父)、 CreateMessage (子)和DisplayMessages (子)。 所有三個組件將在下面顯示。

用戶使用CreateMessage組件創建消息。 它將它保存在 useState 掛鈎indivMessages中,存儲在父Chat組件中。

indivMessages被發送到DisplayMessages組件。 它根據用戶 ID 顯示消息以及同一作者的消息分組。

問題是, indivMessages state 正在使用formattedMessages的值進行設置,該值僅在DisplayMessagesuseEffect掛鈎內設置。

為什么indivMessages設置為formattedMessages的值?

為了這個例子,我注釋掉了所有套接字的東西,以便在無菌環境中工作 - 兩種方式都會發生相同的結果。

聊天.js

import React, { useState, useEffect, useContext } from "react";
import { useSelector } from "react-redux";
import { SocketContext } from "src/SocketContext";

import CreateMessage from "./details/CreateMessage";
import DisplayMessages from "./details/DisplayMessages";

export default function Chat(props) {
  const state = useSelector((state) => state);
  const [indivMessages, setIndivMessages] = useState([]);
  const socket = useContext(SocketContext);

  // useEffect(() => {
  //   if (state.chatShow) {
  //     socket.emit("SUBSCRIBE_CHAT", state.chat.chatRoom);
  //     return () => {
  //       socket.emit("UNSUBSCRIBE", state.chat.chatRoom);
  //     };
  //   }
  // });

  // useEffect(() => {
  //   socket.on("new_message", (data) => {
  //     setIndivMessages([...indivMessages, data]);
  //   });
  //   return () => {
  //     socket.off("new_message");
  //   };
  // }, [socket, indivMessages]);

  return (
    <div className="d-flex flex-column h-100 justify-content-end">
      <DisplayMessages state={state} indivMessages={indivMessages} />
      <CreateMessage
        state={state}
        indivMessages={indivMessages}
        setIndivMessages={setIndivMessages}
      />
    </div>
  );
}

CreateMessage.js

import React, { useState, useContext } from "react";

import { CInputGroup, CInput, CInputGroupAppend, CButton } from "@coreui/react";
import CIcon from "@coreui/icons-react";
import { SocketContext } from "src/SocketContext";

export default function CreateMessage(props) {
  const { indivMessages, setIndivMessages, state } = props;
  const [newMessage, setNewMessage] = useState("");
  const socket = useContext(SocketContext);

  const sendMessage = () => {
    let messageTemplate = {
      messages: [{ msg: newMessage }],
      username: state.user.username,
      _id: indivMessages.length + 1,
      ownerId: state.user._id,
      picture: state.user.picture,
      chatRoom: state.chat.chatRoom,
      date: Date.now(),
    };
    // socket.emit("create_message", messageTemplate);
    setIndivMessages((msgs) => [...msgs, messageTemplate]);
    document.getElementById("msgInput").value = "";
  };

  return (
    <CInputGroup style={{ position: "relative", bottom: 0 }}>
      <CInput
        type="text"
        style={{ fontSize: "18px" }}
        id="msgInput"
        className="rounded-0"
        placeholder="Type a message here..."
        autoComplete="off"
        onChange={(e) => setNewMessage(e.target.value)}
        onKeyUp={(e) => e.code === "Enter" && sendMessage()}
      />
      <CInputGroupAppend>
        <CButton color="success" className="rounded-0" onClick={sendMessage}>
          <CIcon name="cil-send" />
        </CButton>
      </CInputGroupAppend>
    </CInputGroup>
  );
}

DisplayMessages.js

import React, { useEffect, useState } from "react";

import { CContainer, CCard, CImg, CCol, CLabel } from "@coreui/react";

export default function DisplayMessages(props) {
  const { indivMessages, state } = props;
  const [formattedMessages, setFormattedMessages] = useState([]);

  useEffect(() => {
    //Create Grouped Messesges
    let messagesArray = [...indivMessages];
    let sortedArray = messagesArray.sort((a, b) => {
      return new Date(a.date) - new Date(b.date);
    });

    let grouped = [];

    for (let i = 0; i < sortedArray.length; i++) {
      let index = grouped.length - 1;
      if (sortedArray[i].ownerId === grouped[index]?.ownerId) {
        let lastMessage = grouped.pop();
        sortedArray[i].messages[0]._id = sortedArray[i]._id;
        lastMessage.messages = [
          ...lastMessage.messages,
          sortedArray[i].messages[0],
        ];
        grouped.push(lastMessage);
      } else {
        console.log(i, grouped.length);
        grouped.push(sortedArray[i]);
      }
    }
    setFormattedMessages(grouped);
  }, [indivMessages]);

  useEffect(() => {
    let msgContainer = document.getElementById("msgContainer");
    msgContainer.scrollTop = msgContainer.scrollHeight;
  }, [formattedMessages]);

  return (
    <CContainer
      className="mt-2 no-scroll-bar"
      style={{ overflow: "auto", maxHeight: "85vh" }}
      id="msgContainer"
    >
      {formattedMessages.map((msg) => {
        return (
          <CCard
            key={msg._id}
            className="d-flex flex-row p-2"
            color="secondary"
            accentColor={state.user._id === msg.ownerId && "primary"}
          >
            <CImg
              src={msg.picture}
              alt={msg.owner}
              className="w-25 align-self-start rounded"
            />
            <CCol>
              <CLabel>
                <strong>{msg.username}</strong>
              </CLabel>
              {msg.messages.map((message) => {
                return (
                  <p key={message._id ? message._id : msg._id}>{message.msg}</p>
                );
              })}
            </CCol>
          </CCard>
        );
      })}
    </CContainer>
  );
}

我在想DisplayMessages組件中的useEffect掛鈎中發生了一些事情。 每次indivMessages數組更改時,都會執行此 function。

它通過檢查最新消息的作者 ( ownerId ) 是否與前一條消息的作者相同來創建分組消息。 如果它們相同,它將從消息本身中提取所有消息,並將它們添加到前一條消息的消息鍵中,以創建分組消息。

這個結果是在indivMessages數組中設置的,這是出乎意料的,因為我沒有設置帶有分組消息的indivMessages

感謝您的幫助,並感謝您在正確方向上的一些指示!

useEffect代碼中,您正在修改sortedArray中的對象,這些對象也保存在indivMessages中。 例如:

sortedArray[i].messages[0]._id = sortedArray[i]._id;

您設置sortedArray的代碼只是復制數組,而不是其中的對象。 如果要修改這些對象,則需要先制作副本。 例如,上面的示例變為:

sortedArray[i] = {
    ...sortedArray[i],
    messages: [{...sortedArray[i].messages[0], _id = sortedArray[i]._id}, ...sortedArray[i].messages.slice(1)],
};

...或類似的。

你有同樣的問題:

lastMessage.messages = [
  ...lastMessage.messages,
  sortedArray[i].messages[0],
];

...但可能想在其他地方解決它(也許通過將grouped.push(sortedArray[i]);更改為grouped.push({...sortedArray[i]}); ,但我還沒有真正深入閱讀該代碼)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM