Я получаю API, и данные сохраняются в состоянии результата. состояние списка — это массив, состоящий из объекта с идентификатором, результатом и ответным вводом. Метод addItem помещает объект в arrayList, и он помещается в состояние списка. состояние списка с методом .map отображается в конце в ответ внутри контейнера после вызова addItem(), который является кнопкой отправки.
Только первый индекс list state array
, который был получен из API, не получает данные, хранящиеся в результате, и все последующие индексы содержат данные из API.
Я пробовал setTimeout, но это не сработало. Метод .map находится при возврате компонента реакции. Я посмотрел на консоль, и кажется, что первый рендеринг, API загружается и ожидает несколько секунд, но контейнер уже добавлен на веб-страницу, и поэтому на нем нет никаких данных. Целый день пытаюсь разобраться и не могу найти решение.
export default function CreatePrompt() {
const [responseInput, setResponseInput] = useState("");
const [result, setResult] = useState("");
const [list, setList] = useState([
{
id: 101,
inputPrompt: "What is on your mind?",
value: "You are on my mind.",
}
]);
const onSubmit = (event) => {
event.preventDefault()
const getData = async () => {
const data = {
prompt: responseInput,
temperature: 0.5,
max_tokens: 64,
top_p: 1.0,
frequency_penalty: 0.0,
presence_penalty: 0,
};
try {
const response = await fetch('*********', {
method: 'POST',
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.REACT_APP_USER_KEY}`
},
body: JSON.stringify(data),
})
if (response.ok) {
const jsonResponse = await response.json();
setResult(jsonResponse.choices[0].text)
setResponseInput(responseInput)
}
} catch (error) {
console.info(error);
}
}
getData()
}
const addItem = () => {
if (responseInput !== '') {
const promptObj = {
id: Math.random(),
inputPrompt: responseInput,
value: result,
}
const arrayList = [...list];
arrayList.push(promptObj);
setList(arrayList);
setResponseInput('');
}
}
const deleteItem = (key) => {
const arrayList = [...list]
const updateList = arrayList.filter(item => item.id !== key);
setList(updateList);
}
return (
<div>
<main className = "ui container heading-container">
<h1 id = "heading">Fun With AI</h1>
<p id = "command-description">Hello, my name is GPT-3. Ask me to do something.</p>
<div className = "ui form">
<form className = "field" onSubmit = {onSubmit}>
<label id = "prompt-subheading">Enter Prompt</label>
<div className='ui input focus'>
<textarea
type='text'
value = {responseInput}
onChange = {(e) => setResponseInput(e.target.value)}>
</textarea>
</div>
<button
className = "ui button blue right floated"
id = "submit-button"
onClick = {addItem}
>Submit</button>
</form>
</div>
</main>
<div className = "ui container subheading-container">
<h2 id = "response-subheading">Responses</h2>
</div>
<div>
{list.map(item => {
return (
<div
className = "ui container new-response-container"
key = {item.id}
>
<div>
<button
className = "ui right floated button grey mini delete-button"
onClick = {() => deleteItem(item.id)}
>X
</button>
</div>
<div className = "info-container">
<div className = "prompt-container">
<h3 className = "response-title">Prompt:</h3>
<p id = "prompt-input">
{item.inputPrompt}
</p>
</div>
<div className = "response-container">
<h3 className = "response-title">Response:</h3>
<p id = "response-result">{item.value}</p>
</div>
</div>
</div>
)
}).reverse()}
</div>
</div >
)
}
Вы имеете в виду рендеринг состояния list
? В массиве состояний list
есть только один элемент, есть ли проблема с его рендерингом? Вы пытаетесь обновить состояние list
где-то в своем коде, который вы не включили в минимальный, полный и воспроизводимый пример кода? Пожалуйста, отредактируйте свой вопрос, чтобы включить весь соответствующий код, с которым у вас возникли проблемы при работе.
Я не могу найти, где вы используете addItem
или setList
, кроме как в addItem
@ Браво, да, я снял это, проверял, решило ли это мою проблему.
@DrewReese состояние списка отображается как контейнер. Он отображается нормально, но первый контейнер не содержит данных API, но весь контейнер после этого смог отобразить данные.
@Konrad, извините, я не указал эту часть кода в этом посте, но функция addItem вызывается после нажатия кнопки отправки. Просто добавил в код моего поста
Мне непонятно, что вы называете «контейнерами». Вы имеете в виду сопоставленный массив list
, который вы визуализируете?
@DrewReese да, контейнер представляет собой массив отображаемых списков. извините за путаницу
Форма вызывает свой обработчик onSubmit
одновременно и совершенно независимо от обработчика onClick
кнопки отправки, который вызывает addItem
. Кажется, вы хотите или должны вызвать addItem
из обратного вызова onSubmit
после получения данных.
@DrewReese после перехода с onClick
на onSubmit
не отображает его на странице
Из того, что я могу сказать, кажется, вы хотите, чтобы пользователь ввел какое-то значение во ввод textarea
, который используется в обработчике onSubmit
, а затем используйте onSubmit
делает запрос POST с введенными данными и addItem
для обновления состояния list
с введенным responseInput
и разрешенное значение ответа.
Проблема с текущим кодом заключается в том, что оба onSubmit
(через обработчик form
элемента onSubmit
) иaddItem
(через обработчик onClick
кнопки отправки) вызываются одновременно. Другими словами, addItem
использует необновленное значение состояния result
до того, как onSubmit
сделает запрос POST и получит ответ.
Вызовите addItem
или обработайте эту логику синхронно в обработчике обратного вызова onSubmit
. Я также предлагаю использовать служебную функцию для создания GUID, что угодно лучше, чем Math.random()
.
Обратите внимание, что это предлагаемое решение полностью удаляет функцию промежуточного result
состояния иaddItem
, поскольку значение ответа полностью обрабатывается в onSubmit
обработчике.
Пример:
import { v4 as uuidV4 } from 'uuid';
export default function CreatePrompt() {
const [responseInput, setResponseInput] = useState("");
const [list, setList] = useState([
{
id: uuidV4(),
inputPrompt: "What is on your mind?",
value: "You are on my mind.",
}
]);
const onSubmit = (event) => {
event.preventDefault()
const getData = async () => {
const data = {
prompt: responseInput,
temperature: 0.5,
max_tokens: 64,
top_p: 1.0,
frequency_penalty: 0.0,
presence_penalty: 0,
};
try {
const response = await fetch('*********', {
method: 'POST',
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.REACT_APP_USER_KEY}`
},
body: JSON.stringify(data),
});
if (response.ok) {
const jsonResponse = await response.json();
const promptObj = {
id: uuidV4(),
inputPrompt: responseInput, // save current prompt input
value: jsonResponse.choices[0].text, // save resolved response value
}
setList(list => [...list, promptObj); // enqueue list state update
setResponseInput(''); // clear out prompt value
}
} catch (error) {
console.info(error);
}
};
if (responseInput) {
getData();
}
};
Удалите обработчик onClick
кнопки отправки. Обратите внимание, что хотя type = "submit"
является значением атрибута type
по умолчанию, в любом случае лучше указать явно, указав type = "submit"
, чтобы будущим читателям вашего кода было понятно, что кнопка отправляет форму.
<main className = "ui container heading-container">
<h1 id = "heading">Fun With AI</h1>
<p id = "command-description">Hello, my name is GPT-3. Ask me to do something.</p>
<div className = "ui form">
<form className = "field" onSubmit = {onSubmit}>
<label id = "prompt-subheading">Enter Prompt</label>
<div className='ui input focus'>
<textarea
type='text'
value = {responseInput}
onChange = {(e) => setResponseInput(e.target.value)}
/>
</div>
<button
className = "ui button blue right floated"
id = "submit-button"
type = "submit"
>
Submit
</button>
</form>
</div>
</main>
Спасибо за помощь, но решение не работает. Не уверен, упоминал ли я об этом ранее, но после того, как пользователь введет текст в текстовую область и нажмет кнопку отправки, он извлечет данные и должен создать контейнер, который отображает исходное приглашение из текстовой области с ответом (данные, которые извлекается из API). Этот контейнер отображается на веб-странице и остается там. Любое новое приглашение создаст другой контейнер и отобразит его на странице, поэтому у меня есть метод addItem
. После этого пользователь может вручную удалить контейнер, метод deleteItem
Метод addItem
не является частью метода onSubmit
. Вот почему у меня есть onClick
на кнопке, чтобы вызвать метод addItem и отобразить контейнер всех данных из list state
на веб-странице.
@ Джейсон Хорошо. Можете ли вы объяснить свой UX тогда? Какой смысл в состоянии result
и форме onSubmit
, если вы не используете это в пользовательском интерфейсе? Из того, что вы описываете, я не понимаю, как решение, которое я предложил не, делает именно это. Вы хотите, чтобы введенное приглашение отображало немедленно, а ответ POST позже заполнялся им? Не могли бы вы создать демонстрацию Бег codeandbox вашего кода, которую мы могли бы проверять и отлаживать вживую?
не уверен, что вы взглянули на мой развернутый сайт, который я разместил выше. Отправьте несколько случайных запросов, и вы увидите, что на веб-страницу добавляется дополнительный контейнер, поэтому у меня есть метод addItem. Единственная проблема — это первая отправка, она не отображает полученные данные, хотя они получены. Любая отправка после этого отображает данные. Предложенное вами решение удалило этот метод. Здесь уже поздно. Я попробую ваше решение завтра и посмотрю. Оцените всю помощь до сих пор.
@ Джейсон Я посмотрел на случай, если я что-то неправильно понял или пропустил. Правильно, мое решение удаляет функцию addItem
и интегрирует ее логику в обратный вызов onSubmit
, чтобы он мог ожидать ответа POST, который добавляется к элементу в массиве list
. Описанное вами поведение является ошибкой off-by-one
, когда каждый элемент списка использует ранее отправленное значение ответа POST, поэтому вы не видите его для первого сделанного запроса.
Я повторю попытку завтра. Есть вероятность, что я что-то упустил. Я дам вам знать, если это решит мою проблему. Спасибо еще раз.
@ Джейсон Не беспокойся. Если у вас все еще есть проблема, если можете, попробуйте создать работающий пример песочницы, который мы можем изучить. Может быть, это просто что-то тривиальное, что мы упустили из виду.
Сегодня утром я попробовал ваш код, и он сработал! Только одно незначительное изменение в вашем коде. У вас было setList(list => [...list, inputPrompt]);
, но вместо этого должно было быть setList(list => [...list, prompObj]);
, но в целом это решило мою проблему. Благодарю вас!
@ Джейсон Ой, извини за эту небольшую путаницу. Рад, что теперь у вас все работает. Здоровья и удачи.
не ваша проблема, но вам не нужно
await jsonResponse.choices[0].text)
... посколькуjsonResponse
- это просто старый объект javascript, проанализированный из JSON, возвращенного запросом, поэтому не имеетPromise
s дляawait