React – 使用useState时元素消失

huangapple go评论60阅读模式
英文:

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 &lt; and &quot;, 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 &quot;react&quot;;
import { PaperAirplaneIcon } from &quot;@heroicons/react/20/solid&quot;;
import axios from &quot;axios&quot;;
import UserMessage from &quot;./UserMessage&quot;;
import BotMessage from &quot;./BotMessage&quot;;
const Chat = () =&gt; {
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState(&quot;&quot;);
const [loading, setLoading] = useState(false);
const [rules, setRules] = useState([]);
const handleInputSubmit = async (e) =&gt; {
e.preventDefault();
setLoading(true);
const userMessage = { type: &quot;user&quot;, text: inputValue };
setMessages([...messages, userMessage]);
setInputValue(&quot;&quot;);
const botMessage = await getBotMessage(inputValue);
setMessages([...messages, botMessage]);
setLoading(false);
};
const getBotMessage = async (userMessage) =&gt; {
try {
const response = await axios.post(&quot;http://&lt;url&gt;/api/chat&quot;, {
message: userMessage,
});
console.log(&quot;response&quot;, response.data);
const botMessage = { type: &quot;bot&quot;, text: response.data.response };
return botMessage;
} catch (e) {
console.log(e);
const botMessage = {
type: &quot;bot&quot;,
text: &quot;Something went wrong. Try again.&quot;,
};
return botMessage;
}
};
return (
&lt;div className=&quot;flex flex-col items-center w-full h-full justify-between&quot;&gt;
&lt;h1 className=&quot;text-3xl font-bold my-5&quot;&gt;Chatbot&lt;/h1&gt;
&lt;div className=&quot;w-full&quot;&gt;
{messages.map((message, index) =&gt; {
if (message.type === &quot;user&quot;) {
return &lt;UserMessage key={index} text={message.text} /&gt;;
}
return &lt;BotMessage key={index} text={message.text} /&gt;;
})}
&lt;form
onSubmit={handleInputSubmit}
className=&quot;flex flex-row w-full mb-5 mt-2&quot;
&gt;
&lt;input
className=&quot;border-2 rounded-md p-2 w-full&quot;
type=&quot;text&quot;
value={inputValue}
onChange={(e) =&gt; setInputValue(e.target.value)}
/&gt;
&lt;button type=&quot;submit&quot;&gt;
&lt;PaperAirplaneIcon className=&quot;w-6 h-6 text-green-500&quot; /&gt;
&lt;/button&gt;
&lt;/form&gt;
&lt;/div&gt;
&lt;/div&gt;
);
};
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 =&gt; [...prevState, userMessage]);
...
setMessages(prevState =&gt; [...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) =&gt; {
e.preventDefault();
setLoading(true);
const userMessage = { type: &quot;user&quot;, text: inputValue };
setMessages([...messages, userMessage]);
setInputValue(&quot;&quot;);
const botMessage = await getBotMessage(inputValue);
setMessages([...messages, userMessage, botMessage]); // &lt;-- This fixes it.
setLoading(false);
};

If anyone can explain or provide a link to documentation explaining this, I would very much appreciate it.

huangapple
  • 本文由 发表于 2023年4月19日 17:56:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76053148.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定