繁体   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