ReactJS:第一个useEffect在第二个被触发之前不更新状态

ReactJS: First useEffect doesn't update state before the second is triggered

我有一个使用 API 填充 messages 状态的聊天组件,还有不同的区域有不同的聊天,我将其作为道具传递给组件。

在这个组件中我有 3 个 useEffects 但我对其中两个不能正常工作感兴趣。在第一个 useEffect 中,我有一些代码基本上将区域更改时的 messages 状态重置为 undefined。我需要这样做才能区分显示加载组件 <Spinner /> 的 API 尚未被调用,或者 API 是否已被调用并且它已检索到一个空数组显示 <NoData> 组件。

我遇到的问题是,当我更改区域时,useEffects 会按预期触发,但第一个 useEffect 不会在 messages 之前将状态更新为未定义第二个 useEffect 被调用。在由于历史推送而重新呈现后,消息未定义但随后第二个 useEffect 不再被触发。我不明白为什么状态没有在第二个之前的第一个 useEffect 中更新。同样奇怪的是,这以前对我有用,但现在不行了。我在没有推送到 git 的情况下更改了一些内容,现在我很困惑。代码如下:

export default function ChatPage({ history, match, area, ...props }) {
  const [templates, setTemplates] = useState([]);
  const [advisors, setAdvisors] = useState([]);
  const [messages, setMessages] = useState(undefined);
  const [conversation, setConversation] = useState([]);
  const [chatToLoad, setChatToLoad] = useState(false);
  const [isOpen, setIsOpen] = useState(false);
  const [linkOrigin, setLinkOrigin] = useState("");
  const [headerText, setHeaderText] = useState("");

  // useEffect used to reset messages and conversation state
  // triggered on area change(messages and conversation reset)
  // and customer ID change(conversation reset).
  // Required to distinguish between API call not being made yet
  // and API returning no data.
  useEffect(() => {
    if (match.params.id) {
      setLinkOrigin(match.params.id);
    }

    if (messages) {
      if (match.params.id && messages.length !== 0) {
        let matches = messages.filter(
          (message) => message.ORIGINATOR === match.params.id
        );

        if (matches.length !== 0 && match.params.id === linkOrigin) {
          setMessages(undefined);
          history.push("/chats/" + match.params.area);
        }
      }
    }

    setConversation([]);
  }, [area, match.params.id]);

  // API calls
  useEffect(() => {
    if (templates.length === 0) {
      api.getTemplates().then((templates) => {
        setTemplates(templates);
      });
    }

    if (advisors.length === 0) {
      api.getAgents().then((advisors) => {
        setAdvisors(advisors);
      });
    }

    if (!messages || messages.length === 0) {
      chooseQueue(match.params.area).then((queuesData) => {
        let queues = queuesData.data.map((message) => ({
          DATE_SORT: message.DATE_RECIEVED,
          UNIQUEID: message.UNIQUEID,
          ORIGINATOR: message.ORIGINATOR,
          MESSAGE: message.MESSAGE,
          MSG_TYPE: "SMS_OUTBOUND",
          ASSIGNED_TO: message.ASSIGNED_TO || null,
        }));

        setMessages(orderMessagesByDate(queues));
        setChatToLoad(queues[0]);
      });
    }
  }, [area]);

  useEffect(() => {
    if (messages) {
      if (messages.length) {
        let loadId = match.params.id ? match.params.id : messages[0].ORIGINATOR;

        const params = {
          MobileNumber: loadId,
        };
        messagingApi.conversationHistory(params).then((conversationData) => {
          setConversation(
            conversationData.data.map((message) => ({
              DATE_SORT: message.DATE_SORT,
              UNIQUEID: message.UNIQUEID,
              ORIGINATOR: message.ORIGINATOR,
              MESSAGE: message.MESSAGE,
              MSG_TYPE: message.MSG_TYPE2.replace("MobileOriginated", "SMS"),
              ASSIGNED_TO: message.ASSIGNED_TO || null,
            }))
          );
        });

        setChatToLoad(
          messages.find((message) => message.ORIGINATOR === loadId)
        );
        history.push("/chats/" + match.params.area + "/" + loadId);
      }
    }
  }, [messages]);

  function chooseQueue(queueType) {
    switch (queueType) {
      case "myqueue":
        setHeaderText("My chats");
        return queuesApi.getMyActiveQueues(area);
      case "mycompleted":
        setHeaderText("My completed chats");
        return queuesApi.getMyCompletedQueues();
      case "queues":
        setHeaderText("Chats");
        return queuesApi.getQueues(area);
      case "completed":
        setHeaderText("Completed chats");
        return queuesApi.getCompletedQueues();
      default:
        setHeaderText("My chats");
        return queuesApi.getQueues(area);
    }
  }

  function classifyMessage(message) {
    return message.MSG_TYPE.includes("OUTBOUND") ||
      message.MSG_TYPE.includes("FAULT_TEST")
      ? "outbound"
      : "inbound";
  }

  async function submitMessage(message) {
    var params = {
      number: message.ORIGINATOR,
      message: message.MESSAGE,
      smssize: message.MESSAGE.length
    };
    await messagingApi.replyToCustomer(params).then((res) => {
      if (res.data[0].RVALUE === "200") {
        let extendedMsg = [...messages, message];
        let extendedConversation = [...conversation, message];
        setConversation([...extendedConversation]);
        setMessages(orderMessagesByDate([...extendedMsg]));
      }
    });
  }

  function orderMessagesByDate(list) {
    return list.sort(function(x, y) {
      return new Date(y.DATE_SORT) - new Date(x.DATE_SORT);
    });
  }

  const modalHandler = () => {
    setIsOpen(!isOpen);
  };

  let chatConfig = {
    channelSwitch: true,
    channels: channels,
    templateModal: true,
    templates: templates,
    advisorModal: true,
    advisors: advisors,
  };

  const onActiveChatChange = (message) => {
    history.push("/chats/" + match.params.area + "/" + message.ORIGINATOR);

    const params = {
      MobileNumber: message.ORIGINATOR,
    };
    messagingApi.conversationHistory(params).then((conversationData) => {
      setConversation(
        conversationData.data.map((message) => ({
          DATE_SORT: message.DATE_SORT,
          UNIQUEID: message.UNIQUEID,
          ORIGINATOR: message.ORIGINATOR,
          MESSAGE: message.MESSAGE,
          ASSIGNED_TO: message.ASSIGNED_TO || null,
        }))
      );
    });
  };

  return (
    <div data-test="component">
      <BodyHeader
        text={headerText}
        children={
          <FontAwesomeIcon
            icon="plus-square"
            aria-hidden="true"
            size="2x"
            onClick={modalHandler}
          />
        }
      />
      {messages && chatToLoad ? (
        <>
          <ChatWindow
            messages={messages}
            conversation={conversation}
            chatToLoad={chatToLoad}
            onActiveChatChange={onActiveChatChange}
            classifyMessage={classifyMessage}
            submitMessage={submitMessage}
            config={chatConfig}
          />
          <SendMessageModal isOpen={isOpen} toggle={modalHandler} />
        </>
      ) : !messages ? (
        <Spinner />
      ) : (
        <NoDataHeader>There are no chats in this area</NoDataHeader>
      )}
          
    </div>
  );
}

这样你得不到你想要的。在 useEffect 中应用的状态更改直到下一个渲染周期才会生效,以下回调仍将看到当前的 const 值。 如果您想更改当前渲染周期中的值,您唯一的选择就是将您的 const 放宽为 let 并自己设置变量。

毕竟:您期望 const 发生变化,不是吗? ;)