Первый визуализированный контейнер не имеет данных из API-выборки в React

Я получаю 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 >
    )
}

не ваша проблема, но вам не нужно await jsonResponse.choices[0].text) ... поскольку jsonResponse - это просто старый объект javascript, проанализированный из JSON, возвращенного запросом, поэтому не имеет Promises для await

Bravo 16.05.2022 01:39

Вы имеете в виду рендеринг состояния list? В массиве состояний list есть только один элемент, есть ли проблема с его рендерингом? Вы пытаетесь обновить состояние list где-то в своем коде, который вы не включили в минимальный, полный и воспроизводимый пример кода? Пожалуйста, отредактируйте свой вопрос, чтобы включить весь соответствующий код, с которым у вас возникли проблемы при работе.

Drew Reese 16.05.2022 01:40

Я не могу найти, где вы используете addItem или setList, кроме как в addItem

Konrad Linkowski 16.05.2022 01:42

@ Браво, да, я снял это, проверял, решило ли это мою проблему.

Jason 16.05.2022 01:57

@DrewReese состояние списка отображается как контейнер. Он отображается нормально, но первый контейнер не содержит данных API, но весь контейнер после этого смог отобразить данные.

Jason 16.05.2022 01:58

@Konrad, извините, я не указал эту часть кода в этом посте, но функция addItem вызывается после нажатия кнопки отправки. Просто добавил в код моего поста

Jason 16.05.2022 01:59

Мне непонятно, что вы называете «контейнерами». Вы имеете в виду сопоставленный массив list, который вы визуализируете?

Drew Reese 16.05.2022 02:15

@DrewReese да, контейнер представляет собой массив отображаемых списков. извините за путаницу

Jason 16.05.2022 02:16

Форма вызывает свой обработчик onSubmit одновременно и совершенно независимо от обработчика onClick кнопки отправки, который вызывает addItem. Кажется, вы хотите или должны вызвать addItem из обратного вызова onSubmit после получения данных.

Drew Reese 16.05.2022 02:20

@DrewReese после перехода с onClick на onSubmit не отображает его на странице

Jason 16.05.2022 02:31
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
10
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема

Из того, что я могу сказать, кажется, вы хотите, чтобы пользователь ввел какое-то значение во ввод 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

Jason 16.05.2022 05:22

Метод addItem не является частью метода onSubmit. Вот почему у меня есть onClick на кнопке, чтобы вызвать метод addItem и отобразить контейнер всех данных из list state на веб-странице.

Jason 16.05.2022 05:42

@ Джейсон Хорошо. Можете ли вы объяснить свой UX тогда? Какой смысл в состоянии result и форме onSubmit, если вы не используете это в пользовательском интерфейсе? Из того, что вы описываете, я не понимаю, как решение, которое я предложил не, делает именно это. Вы хотите, чтобы введенное приглашение отображало немедленно, а ответ POST позже заполнялся им? Не могли бы вы создать демонстрацию Бег codeandbox вашего кода, которую мы могли бы проверять и отлаживать вживую?

Drew Reese 16.05.2022 05:56

не уверен, что вы взглянули на мой развернутый сайт, который я разместил выше. Отправьте несколько случайных запросов, и вы увидите, что на веб-страницу добавляется дополнительный контейнер, поэтому у меня есть метод addItem. Единственная проблема — это первая отправка, она не отображает полученные данные, хотя они получены. Любая отправка после этого отображает данные. Предложенное вами решение удалило этот метод. Здесь уже поздно. Я попробую ваше решение завтра и посмотрю. Оцените всю помощь до сих пор.

Jason 16.05.2022 06:10

@ Джейсон Я посмотрел на случай, если я что-то неправильно понял или пропустил. Правильно, мое решение удаляет функцию addItem и интегрирует ее логику в обратный вызов onSubmit, чтобы он мог ожидать ответа POST, который добавляется к элементу в массиве list. Описанное вами поведение является ошибкой off-by-one, когда каждый элемент списка использует ранее отправленное значение ответа POST, поэтому вы не видите его для первого сделанного запроса.

Drew Reese 16.05.2022 06:13

Я повторю попытку завтра. Есть вероятность, что я что-то упустил. Я дам вам знать, если это решит мою проблему. Спасибо еще раз.

Jason 16.05.2022 06:16

@ Джейсон Не беспокойся. Если у вас все еще есть проблема, если можете, попробуйте создать работающий пример песочницы, который мы можем изучить. Может быть, это просто что-то тривиальное, что мы упустили из виду.

Drew Reese 16.05.2022 06:16

Сегодня утром я попробовал ваш код, и он сработал! Только одно незначительное изменение в вашем коде. У вас было setList(list => [...list, inputPrompt]); , но вместо этого должно было быть setList(list => [...list, prompObj]); , но в целом это решило мою проблему. Благодарю вас!

Jason 16.05.2022 15:16

@ Джейсон Ой, извини за эту небольшую путаницу. Рад, что теперь у вас все работает. Здоровья и удачи.

Drew Reese 16.05.2022 16:47

Другие вопросы по теме