简体   繁体   English

API 没有调用第一次渲染,只有在我手动重新渲染 React 应用程序之后

[英]API is not calling on first render, only after I manually re-render React app

I am calling the Azure Optical Character Recognition API in my React app that requires an initial POST request to get an 'operation-location' value from the response header. The 'operation-location' value is then used to make a GET request.我在我的 React 应用程序中调用 Azure 光学字符识别 API,它需要初始 POST 请求以从响应 header 中获取“操作位置”值。然后使用“操作位置”值发出 GET 请求。

The API takes an url to an image with text and returns the plain text from the image. API 将 url 用于带文本的图像,并返回图像中的纯文本。

The issue I am facing is that the POST request is only successful when I manually re-render the React app .我面临的问题是POST 请求只有在我手动重新渲染 React 应用程序时才会成功 I submit this url https://i.stack.imgur.com/i1Abv.png via the UI I have created, both POST and GET requests fail but they will only be successful when I hit ctrl + S on the development environment.我通过我创建的 UI 提交了这个 url https://i.stack.imgur.com/i1Abv.png,POST和 GET 请求都失败了,但只有当我在开发环境中按 ctrl + S 时它们才会成功。

When I log the 'operation-location' value on the first render, it returns null so the GET request is made to http://localhost:5173/null and so it fails.当我在第一次呈现时记录“操作位置”值时,它返回 null,因此向 http://localhost:5173/null 发出 GET 请求,因此失败。 But, when I manually re-render the app by hitting save, both API calls are successful and the getText variable is rendered.但是,当我通过点击保存手动重新呈现应用程序时,两个 API 调用都成功并且呈现了getText变量。

getText is a stateful variable that I am updating with the text from the image getText是一个有状态变量,我正在用图像中的文本更新它

I am trying to make it so it is successful on the first load.我正在努力让它在第一次加载时成功。

On the first render, I get an error TypeError: Cannot read properties of undefined (reading 'analyzeResult') when trying to resolve .then((json)=> { within the getData function在第一次渲染时,我收到错误 TypeError: Cannot read properties of undefined (reading 'analyzeResult') when trying to resolve .then((json)=> { within the getData function

The POST request takes a user input in the body of the request from props.UserUrl which comes from the file Homepage.jsx: POST 请求在来自文件 Homepage.jsx 的 props.UserUrl 的请求正文中获取用户输入:

import {React, useState} from 'react'
import Api from './Api';

export default function Homepage(props){
const [isClicked, setIsClicked] = useState(false)
const [input, setInput] = useState('');
const [text, setText] = useState('')

function handleChange (e) {
    setInput(e.target.value)
}

function handleClick(){
    setText(input)
    setIsClicked(true);

} 
        {if (isClicked == true){
            return (
                <div>
                     <Api UserUrl={text}/>
                </div>             
            )
        } else {
            return (
                <div className="image-input">
                    <input type="text" placeholder='enter url you want to convert' onChange={handleChange}/>
                    <button onClick={handleClick}>Submit</button>
                    </div>
                    )
        }
        }

} }

The API is called in Api.jsx: API在Api.jsx中调用:

import { React, useState, useEffect } from 'react';

export default function Api(props) {
  // Need this unique value to send the GET request. We receive it from the POST request
  const [operationLocation, setOperationLocation] = useState(null);

 
  // We will add the text from the image to this variable
  const [getText, setGetText] = useState(null);

  const [userUrl, setUserUrl] = useState('');

  const [loading, setLoading] = useState(false);

  const [responseHeaders, setResponseHeaders] =useState ({})


  // We make a POST request to this URL
  let url = `${import.meta.env.VITE_ENDPOINT}.cognitiveservices.azure.com/vision/v3.2/read/analyze`;
  // Options for POST request
  const options = {
    // Change the image url to any you like that has text
    body: (JSON.stringify({ "url": props.UserUrl })),
    headers: {
      // Need to set this parameter or else won't work due to CORS
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
      // This is the value we need to get in the header request
      "Access-Control-Expose-Headers": "operation-location",
      "Content-Type": "application/json",
      "Ocp-Apim-Subscription-Key": import.meta.env.VITE_API_KEY
    },
    method: "POST",
    mode: 'cors',
    credentials: "same-origin"
  };


  // Options for Get request
  const optionsGet = {
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
      'Accept': 'application/json',
      "Content-Type": "application/json",
      "Ocp-Apim-Subscription-Key": import.meta.env.VITE_API_KEY
    },
    method: "GET",
    mode: 'cors',
    credentials: "same-origin"
  };

  useEffect(()=> {
    // Post request
    const fetchData = async () => {
      setLoading(true)
      fetch(url, options)
      // Setting the operation location value from the header
      .then((res) => {
        return setOperationLocation(res.headers.get('operation-location'))
      })   
      // On first render, operationLocation value is not being returned, returns null
       .then(console.log(operationLocation))
         }
        
        // Get request
      const getData = () => {
        // Passing the operation location value from the POST request into our GET request
        fetch(operationLocation, optionsGet)
        .then((res) => {
          if (res.ok) {
            return res.json()
          }    
        })
        .then((json)=> {
          // The response returns an array of text. The text gets broken up into multiple lines
          const textArray = json.analyzeResult.readResults[0].lines;
          // Map through the response to get each individual text
          const textOutput = textArray.map(item => item.text) //returns an array
          // Use the join method to get rid of the commas in the array so it returns a string
          const textOutputString = textOutput.join(' ')
          setGetText(textOutputString)
  
          setLoading(false)
        })
        .catch(err => {
          console.log(err)
        })
        
      }  
    // Asyncrhonously make the requests. We need to wait for response from the POST request before we execute the GET request
      async function callApi () {

        await fetchData ()
        console.log('post request successful')
        console.log(operationLocation)
        await getData()
        console.log('get request succesful')
      setLoading(false)
    } 
      callApi();
  
  }, [props.UserUrl])
  
  // We check to see if the request has populate state and then we render the response
  if (loading) {
      return (
       <div>
        Loading...
       </div>
      )} else {
        return (
           <div className="center">
        <p>{getText}</p>
      <p>{props.UserUrl}</p>
    </div>
        )
      }
    }

What I have tried:我试过的:

  • The url for the POST request is correct because it will render successfully in its second render. POST 请求的 url 是正确的,因为它将在其第二次呈现中成功呈现。 I have moved it to a.env variable because the endpoint is sensitive我已将其移至 a.env 变量,因为端点是敏感的
  • I have tried to make the GET request asynchronous but I am not sure if GET request is being made before the POST request is successful.我试图使 GET 请求异步,但我不确定在 POST 请求成功之前是否正在发出 GET 请求。 On checking the.network tab, it looks like the POST request takes 956ms to be successful.在检查 .network 选项卡时,POST 请求似乎需要 956 毫秒才能成功。
  • I don't think it's a CORS problem because I have set the mode to CORS and also I have used an Edge plugin to disable CORS and I was still having the same problem我不认为这是 CORS 的问题,因为我已将模式设置为 CORS 并且我还使用了 Edge 插件来禁用 CORS 我仍然遇到同样的问题
  • I have tried using a stateful variable called loading to re-render React on API call but still facing same issue of it only working on a manual re-render我尝试使用一个名为loading的有状态变量在 API 调用上重新渲染 React,但仍然面临同样的问题,它只能在手动重新渲染时工作
  • I have tried using a stateful variable called data to re-render React on API call but still facing same issue of it only working on a manual re-render我尝试使用一个名为data的有状态变量在 API 调用上重新渲染 React,但仍然面临同样的问题,它只适用于手动重新渲染
  • I tried using props.UserUrl as a stateful variable but was running into an endless loop我尝试使用props.UserUrl作为有状态变量,但遇到了无限循环
  • I have tried using a stateful variable to capture the responseHeaders to force the API to re-render but I was still facing the same issues我尝试使用有状态变量来捕获responseHeaders以强制 API 重新呈现,但我仍然面临同样的问题
  • I added operationLocation value to the useEffect array but it created an endless loop我将operationLocation值添加到 useEffect 数组,但它创建了一个无限循环
  • I have tried using location.reload() to re-render React but it just reloaded back to the initial UI and lost the data我尝试使用location.reload()重新渲染 React,但它只是重新加载回初始 UI 并丢失了数据

I think I need to force React to re-render someway but I don't know how.我想我需要强制 React 以某种方式重新渲染,但我不知道该怎么做。

add the POST request in another useEffect with no dependency so the useEffect will run only once when the app load first time在另一个没有依赖关系的 useEffect 中添加 POST 请求,这样 useEffect 只会在应用程序第一次加载时运行一次

 useEffect(()=> {
    // Post request
    const fetchData = async () => {
      setLoading(true)
      fetch(url, options)
      // Setting the operation location value from the header
      .then((res) => {
        return setOperationLocation(res.headers.get('operation-location'))
      })   
      // On first render, operationLocation value is not being returned, returns null
       .then(console.log(operationLocation))
         }
  
  }, [])


 useEffect(()=> {
        // Get request
      const getData = () => {
        // Passing the operation location value from the POST request into our GET request
        fetch(operationLocation, optionsGet)
        .then((res) => {
          if (res.ok) {
            return res.json()
          }    
        })
        .then((json)=> {
          // The response returns an array of text. The text gets broken up into multiple lines
          const textArray = json.analyzeResult.readResults[0].lines;
          // Map through the response to get each individual text
          const textOutput = textArray.map(item => item.text) //returns an array
          // Use the join method to get rid of the commas in the array so it returns a string
          const textOutputString = textOutput.join(' ')
          setGetText(textOutputString)
  
          setLoading(false)
        })
        .catch(err => {
          console.log(err)
        })
        
      }  
    // Asyncrhonously make the requests. We need to wait for response from the POST request before we execute the GET request
      async function callApi () {

        await fetchData ()
        console.log('post request successful')
        console.log(operationLocation)
        await getData()
        console.log('get request succesful')
      setLoading(false)
    } 
      callApi();
  
  }, [props.UserUrl])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM