Next.js/React状态数组两次追加值被覆盖问题求助
问题:ChatGPT聊天机器人状态数组更新异常,AI回复覆盖用户消息
开发Next.js/React简易ChatGPT聊天机器人时,点击发送按钮后,用户输入消息本应先加入状态数组,再追加ChatGPT的API回复,但实际AI回复会覆盖用户消息,仅显示后者。怀疑是闭包导致状态更新引用旧数组,且React状态更新为异步,搜索后未解决。相关代码如下:
import {useState} from "react"; export default function Chatbot() { const [inputMessage, setInputMessage] = useState(''); const [messages, setMessages] = useState([]); /** * Push a new message to array of messages * @param {string} text */ const addMessage = (text) => { setMessages([ ...messages, text, ]) } const queryChatbot = () => { // Add input message / question as new message addMessage(inputMessage); fetch('/api/chatbot', { method: 'post', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ inputMessage, }), }) .then(response => response.json()) .then(data => { if (data.response) { // For successful response: Add response as new message addMessage(data.response.output.response); } else { // Otherwise add error as new message addMessage('Sorry, no response possible.'); } }) .catch(error => { // In error case we add a new message with an error addMessage('Sorry, an error ocurred.'); }); } return <> <input type="text" placeholder="Send a message" onChange={(event) => setInputMessage(event.target.value)} /> <button onClick={queryChatbot}>Send message</button> {messages.map(message => ( <p>{ message }</p> ))} </> }
原因分析
问题核心在于React状态更新的异步性和闭包捕获旧值:
addMessage函数中直接使用当前渲染周期的messages状态值生成新数组- 调用
queryChatbot时,先执行addMessage(inputMessage),但React状态更新是异步的,messages不会立即更新 - 后续
fetch是异步操作,当回调执行时,queryChatbot闭包中捕获的messages仍是初始的旧值(空数组) - 第二次调用
addMessage时,会基于旧的空数组生成新数组,直接覆盖第一次更新的结果,最终仅显示AI回复
解决方法
使用React状态更新的函数式更新方式,确保每次更新都基于最新的前一次状态,而非闭包中的旧值:
修改addMessage函数
将setMessages的参数改为函数,接收上一次状态prevMessages,基于它生成新数组:
const addMessage = (text) => { setMessages(prevMessages => [...prevMessages, text]); }
额外优化:给列表项添加唯一key
React渲染列表时需要唯一key来优化性能、避免警告,修改map部分:
{messages.map((message, index) => ( <p key={index}>{ message }</p> ))}
完整修改后的代码
import {useState} from "react"; export default function Chatbot() { const [inputMessage, setInputMessage] = useState(''); const [messages, setMessages] = useState([]); const addMessage = (text) => { setMessages(prevMessages => [...prevMessages, text]); } const queryChatbot = () => { addMessage(inputMessage); fetch('/api/chatbot', { method: 'post', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ inputMessage, }), }) .then(response => response.json()) .then(data => { if (data.response) { addMessage(data.response.output.response); } else { addMessage('Sorry, no response possible.'); } }) .catch(error => { addMessage('Sorry, an error occurred.'); }); } return <> <input type="text" placeholder="Send a message" onChange={(event) => setInputMessage(event.target.value)} /> <button onClick={queryChatbot}>Send message</button> {messages.map((message, index) => ( <p key={index}>{ message }</p> ))} </> }
内容的提问来源于stack exchange,提问作者matthib
相关产品推荐
相关产品推荐

