英文:
React - elements disappearing with useState
问题
I am trying to learn React and build a simple, but specialized, chatbot using OpenAI API along the way. I am currently able to send and receive messages, but I encounter an issue when displaying the messages. When I send a user message, the user message will display on the page, but only until the response arrives, at which the user message disappears.
My Chat component is shown below. My guess was that setMessages([...messages, botMessage]);
simply appended a message to the state of the current list of messages, though maybe I have misunderstood something there.
import React, { useState } from "react";
import { PaperAirplaneIcon } from "@heroicons/react/20/solid";
import axios from "axios";
import UserMessage from "./UserMessage";
import BotMessage from "./BotMessage";
const Chat = () => {
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState("");
const [loading, setLoading] = useState(false);
const [rules, setRules] = useState([]);
const handleInputSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const userMessage = { type: "user", text: inputValue };
setMessages([...messages, userMessage]);
setInputValue("");
const botMessage = await getBotMessage(inputValue);
setMessages([...messages, botMessage]);
setLoading(false);
};
const getBotMessage = async (userMessage) => {
try {
const response = await axios.post("<url>/api/chat", {
message: userMessage,
});
console.log("response", response.data);
const botMessage = { type: "bot", text: response.data.response };
return botMessage;
} catch (e) {
console.log(e);
const botMessage = {
type: "bot",
text: "Something went wrong. Try again.",
};
return botMessage;
}
};
return (
<div className="flex flex-col items-center w-full h-full justify-between">
<h1 className="text-3xl font-bold my-5">Chatbot</h1>
<div className="w-full">
{messages.map((message, index) => {
if (message.type === "user") {
return <UserMessage key={index} text={message.text} />;
}
return <BotMessage key={index} text={message.text} />;
})}
<form
onSubmit={handleInputSubmit}
className="flex flex-row w-full mb-5 mt-2"
>
<input
className="border-2 rounded-md p-2 w-full"
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button type="submit">
<PaperAirplaneIcon className="w-6 h-6 text-green-500" />
</button>
</form>
</div>
</div>
);
};
export default Chat;
(Note: The code you provided appears to contain HTML entities like <
and "
, which should be replaced with the actual characters <
and "
for the code to work correctly.)
英文:
I am trying to learn React and build a simple, but specialized, chatbot using OpenAI API along the way. I am currently able to send and receive messages, but I encounter an issue when displaying the messages. When I send a user message, the user message will display on the page, but only until the response arrives, at which the user message disappears.
My Chat component is shown below. My guess was that setMessages([...messages, botMessage]);
simply appended a message to the state of the current list of messages, though maybe I have misunderstood something there.
import React, { useState } from "react";
import { PaperAirplaneIcon } from "@heroicons/react/20/solid";
import axios from "axios";
import UserMessage from "./UserMessage";
import BotMessage from "./BotMessage";
const Chat = () => {
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState("");
const [loading, setLoading] = useState(false);
const [rules, setRules] = useState([]);
const handleInputSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const userMessage = { type: "user", text: inputValue };
setMessages([...messages, userMessage]);
setInputValue("");
const botMessage = await getBotMessage(inputValue);
setMessages([...messages, botMessage]);
setLoading(false);
};
const getBotMessage = async (userMessage) => {
try {
const response = await axios.post("http://<url>/api/chat", {
message: userMessage,
});
console.log("response", response.data);
const botMessage = { type: "bot", text: response.data.response };
return botMessage;
} catch (e) {
console.log(e);
const botMessage = {
type: "bot",
text: "Something went wrong. Try again.",
};
return botMessage;
}
};
return (
<div className="flex flex-col items-center w-full h-full justify-between">
<h1 className="text-3xl font-bold my-5">Chatbot</h1>
<div className="w-full">
{messages.map((message, index) => {
if (message.type === "user") {
return <UserMessage key={index} text={message.text} />;
}
return <BotMessage key={index} text={message.text} />;
})}
<form
onSubmit={handleInputSubmit}
className="flex flex-row w-full mb-5 mt-2"
>
<input
className="border-2 rounded-md p-2 w-full"
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button type="submit">
<PaperAirplaneIcon className="w-6 h-6 text-green-500" />
</button>
</form>
</div>
</div>
);
};
export default Chat;
答案1
得分: 1
以下是翻译好的内容:
set函数仅更新下一次渲染的状态变量。
如果在调用set函数后读取状态变量,您仍然会得到在您的调用之前显示在屏幕上的旧值。
您对setMessages
的调用不会立即更新message
变量。如果您仔细考虑,这完全合理,因为messages
被定义为const
,所以永远不能重新分配!在下一次重新渲染时,您将获得具有更新值的全新messages
。
因此,您对setMessages
的第二次调用将botMessage
添加到原始数组中,实际上替换了userMessage
。
您可以尝试使用更新函数,如下所示:
setMessages(prevState => [...prevState, userMessage]);
...
setMessages(prevState => [...prevState, botMessage]);
更新函数也会在下一次重新渲染时执行,但它们按队列应用,并始终接收您状态变量的最新值,因此两个元素都将被正确添加。
英文:
You have the explanation in official documentation here:
> The set function only updates the state variable for the next render.
> If you read the state variable after calling the set function, you
> will still get the old value that was on the screen before your call.
Your call to setMessages
doesn't result in your message
variable being immediately updated. If you think about it, it makes total sense as messages
is defined as a const
, so it can never be reassigned! On the next re-render, you'll have a fresh new messages
with the updated value.
So your second call to setMessages
is adding botMessage
to the original array, effectively replacing userMessage
.
Something you could try is using updater functions like:
setMessages(prevState => [...prevState, userMessage]);
...
setMessages(prevState => [...prevState, botMessage]);
Updater functions are also executed on the next re-render, but they are applied in queue and always receive the most recent value of your state variable, so both elements will be properly added.
答案2
得分: -1
以下是您要翻译的内容:
EDIT: The solution below works, but is not the optimal solution. Check the other answer.
As new to React, I don't understand why this is the way it is, but I found the answer.
For some reason the setMessages([...messages, botMessage]);
can't see the updated messages
state array given by setMessages([...messages, userMessage]);
three lines prior (when a user message is sent).
The solution is to add both messages, when the bot message arrives like, so the handleInputSubmit
becomes:
const handleInputSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const userMessage = { type: "user", text: inputValue };
setMessages([...messages, userMessage]);
setInputValue("");
const botMessage = await getBotMessage(inputValue);
setMessages([...messages, userMessage, botMessage]); // <-- This fixes it.
setLoading(false);
};
If anyone can explain or provide a link to documentation explaining this, I would very much appreciate it.
英文:
EDIT: The solution below works, but is not the optimal solution. Check the other answer.
As new to React, I don't understand why this is the way it is, but I found the answer.
For some reason the setMessages([...messages, botMessage]);
can't see the updated messages
state array given by setMessages([...messages, userMessage]);
three lines prior (when a user message is sent).
The solution is to add both messages, when the bot message arrives like, so the handleInputSubmit
becomes:
const handleInputSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const userMessage = { type: "user", text: inputValue };
setMessages([...messages, userMessage]);
setInputValue("");
const botMessage = await getBotMessage(inputValue);
setMessages([...messages, userMessage, botMessage]); // <-- This fixes it.
setLoading(false);
};
If anyone can explain or provide a link to documentation explaining this, I would very much appreciate it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论