呈现的第一个容器没有来自 api 在 React 中获取的数据

First container rendered have no data from api fetch in React

我正在获取一个 api,数据正在以结果状态存储。 list state 是一个数组,包含一个带有 id、result 和 responseinput 的对象。 addItem 方法将对象推入 arrayList 并将其推入列表状态。在调用 addItem() 后,在容器内的 return 末尾呈现具有 .map 方法的列表状态,即提交按钮。

只有从 api 获取的 list state array 的第一个索引没有得到存储在结果中的数据,之后的所有索引确实有来自 [=18= 的数据]. 我试过 setTimeout 但它不起作用。 .map 方法位于 React 组件的 return 处。我查看了控制台,似乎是第一个渲染,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.log(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 >
    )
}

问题

据我所知,您似乎希望用户在 onSubmit 处理程序中使用的 textarea 输入中输入一些值,然后使用 onSubmit 使带有输入数据的 POST 请求和 addItem 使用输入的 responseInput 和解析的响应值更新 list 状态。

当前代码的问题在于 onSubmit通过 form 元素的 onSubmit 处理程序 addItem通过提交按钮的onClick处理程序)被同时调用。换句话说,addItemonSubmit 发出 POST 请求并收到响应之前使用未更新的 result 状态值。

解决方案

调用 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.log(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>