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 发生变化,不是吗? ;)
我有一个使用 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 发生变化,不是吗? ;)