简体   繁体   English

从另一个 API 数据获取 API URL id 无法正常工作

[英]Getting an API URL id from another API data not working properly

In my small react Project, I needed to use an id from one api call in subsequent api calls.在我的小型反应项目中,我需要在随后的 api 调用中使用来自一个 api 调用的 id。 I have access to the data from the first call but do not know how to use it in the second call.我可以访问第一次通话中的数据,但不知道如何在第二次通话中使用它。 I tried using a string literal to access all the ids so the URL can pick each ids directly from the other API but it was just returning random IDs.我尝试使用字符串文字来访问所有 ID,因此 URL 可以直接从另一个 API 中选择每个 ID,但它只是返回随机 ID。

Here is my code below这是我下面的代码


import React, { Component } from "react";
import axios from "axios";
export default class Cat extends Component {
   state = {
   imagedata: [],
   data: [],
   CatWeight: "",
   CatMetric: "",

     }
   
componentDidMount(){
   this.fetchCountryData();
   this.fetchImageUrl();
}     


  fetchCountryData = async () => {
    const url = "https://api.thecatapi.com/v1/breeds";
    try {
      const response = await axios.get(url);
      const data = await response.data;
     console.log(data)
      this.setState({
        data,
      });
    } catch (error) {
      console.log(error);
    }
  };


  fetchImageUrl = async () =>{
    
    // This is the Place I'm having issue with. The line below. where imgurl becomes https://api.thecatapi.com/v1/images/search?breed_id=char as an example
    // The id is coming from the First API id's
  const imgurl = `https://api.thecatapi.com/v1/images/search?breed_id=${this.state.data.map((item) => { return item.id })} ` 

 try {
    const response = await axios.get(imgurl);
    const imagedata = await response.data;
    console.log(imagedata)
    this.setState({
        imagedata,
    });
  } catch (error) {
    console.log(error);
  }
};

   
 render() {
   return (
     <div>
      {
            this.state.imagedata.map((item, id) =>{
            return <ul key={id}>{item.id}</ul>
            })
        
      }
      
     </div>
   )
 }
}




I do not know that specific api, but it looks like that endpoint would only take a single id.我不知道具体的 api,但看起来那个端点只需要一个 id。 By using map, you are giving it an array of ids.通过使用 map,您可以为其提供一个 id 数组。 You may need to make separate api calls for each id.您可能需要为每个 id 进行单独的 api 调用。

( Note : answer significantly updated in response to comment.) 注意:答案已根据评论进行了重大更新。)

There was a typo in your original.你的原文有错别字。 I don't know if that was only in StackOverflow or if it's also in the code that's failing for you.我不知道这是否仅存在于 StackOverflow 中,或者是否也存在于对您而言失败的代码中。 But the search URL looks like但是搜索 URL 看起来像

https://api.thecatapi.com/v1/images/search?breed_ids=ebur

It's breed_ids , plural, not breed_id .它是breed_ids ,复数,而不是breed_id Also, it only takes a single breed id, not a list of them, so you will need separate calls for each.此外,它只需要一个品种 ID,而不是它们的列表,因此您需要为每个品种单独调用。

In the majority of cases (64/67), the image URL is already in the response, and there's no reason to fetch it again.在大多数情况下 (64/67),图像 URL 已经在响应中,没有理由再次获取它。 So we can fetch it only for the few missing ones, and use Promise.all to combine them to handle the output.所以我们可以只为少数缺失的部分获取它,并使用Promise.all将它们组合起来处理 output。 Here's one technique that doesn't try to use your React setup, but a simple innerHTML atop some text manipulation, and using the native fetch instead of axios :这是一种不尝试使用 React 设置的技术,而是在一些文本操作之上使用简单的innerHTML ,并使用本机fetch而不是axios

 fetch('https://api.thecatapi.com/v1/breeds').then (resp => resp.json()).then (breeds => breeds.map ( breed => 'image' in breed && 'url' in breed.image? Promise.resolve ({name: breed.name, url: breed.image.url}): fetch (`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}`).then (resp => resp.json ()).then (res => ({name: breed.name, url: res[0].url})) )).then (xs => Promise.all (xs)).then (breeds => breeds.map( breed => `<img src="${breed.url}" title="${breed.name}" width="100"/>` ).join ('')).then (pics => document.getElementById ('cats').innerHTML = pics).catch (err => console.log (`Error: ${err}`))
 <div id="cats"></div>

Because Promise.all accepts scalar values as well as Promises in its input array, we didn't have to wrap in Promise s those that were in the original document.因为Promise.all在其输入数组中接受标量值和 Promise,所以我们不必将原始文档中的内容包装在Promise中。 Instead of代替

      ? Promise .resolve ({name: breed.name, url: breed.image.url})

we could have just written:我们可以写:

      ? {name: breed.name, url: breed.image.url}

and it would be slightly more performant.它的性能会稍微好一些。 But I still prefer the version as written, just out of a desire for consistency.但我仍然更喜欢书面版本,只是出于对一致性的渴望。

Also, if you're working in an environment that supports the optional chaining operator (most but not yet all common environments), then this此外,如果您在支持可选链接运算符(大多数但不是所有常见环境)的环境中工作,那么这个

    breed => 'image' in breed && 'url' in breed.image

might be better written as可能更好地写为

    breed => breed?.image?.url

Update -- Breaking this down更新——打破这个

A comment asked for a code breakdown.一条评论要求对代码进行细分。 Here is an attempt:这是一个尝试:

fetch('https://api.thecatapi.com/v1/breeds')
  .then (resp => resp.json())

We start by fetching the data and turning the result into an object.我们首先获取数据并将结果转换为 object。 I used the built-in fetch call rather than importing axios, but we could do something very similar with axios.我使用了内置的fetch调用,而不是导入 axios,但我们可以用 axios 做一些非常相似的事情。

Note that we are using straight promises here, and not the async-await gloss on them.请注意,我们在这里使用的是直接的 Promise,而不是对它们的async-await修饰。 This could certainly be rewritten in an aysnc-await style, but, especially when I'm using Promise.all , I don't see much advantage.这当然可以用aysnc-await风格重写,但是,特别是当我使用Promise.all时,我看不到太多优势。

  .then (breeds => breeds .map (

the result we get from the call is an array.我们从调用中得到的结果是一个数组。 We use Array.prototype.map on that array to turn an array of breeds into an array of something else, as seen in the next section:我们在该数组上使用Array.prototype.map将品种数组转换为其他数组,如下一节所示:

    breed => 'image' in breed && 'url' in breed.image
      ? Promise .resolve ({name: breed.name, url: breed.image.url})
      : fetch (`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}`)
          .then (resp => resp .json ())
          .then (res => ({name: breed.name, url: res[0].url}))
  ))

Here we test if the object already has the URL property we want.这里我们测试 object 是否已经具有我们想要的 URL 属性。 If it does, we return an already-resolved Promise for an object with name and url properties found in the breed object from the array.如果是这样,我们返回一个已解析的 Promise 用于 object,其nameurl属性在品种 ZA6696CFDE63131BDC4BEB66 的数组中找到。 If it doesn't (and there are only a few of these in my test), we construct the image search url for the breed, call fetch on that, and when the resulting promise resolves, we turn this response into an object with resp.json () , and finally construct a name / url object from this.如果没有(我的测试中只有少数几个),我们为品种构建图像搜索 url,调用fetch ,当结果 promise 解析时,我们将此响应转换为resp.json () ,最后由此构造一个name / url object。

In either branch of the conditional statement, we are returning a Promise.在条件语句的任一分支中,我们都返回 Promise。 So the result of this is an array of Promises.所以这个结果是一个 Promise 数组。

  .then (xs => Promise.all (xs))

We use Promise.all to convert an array of Promises for our name / url objects into a Promise for an array of name / url objects. We use Promise.all to convert an array of Promises for our name / url objects into a Promise for an array of name / url objects. This Promise will resolve when all the Promises we supplied have resolved, and it will return an array with the results of the calls.这个 Promise 将在我们提供的所有 Promise 都解析后解析,并将返回一个包含调用结果的数组。 For instance, if we had this:例如,如果我们有这个:

 Promise.all([ new Promise ((res, rej) => setTimeout(res, 2000, 'foo')), new Promise ((res, rej) => setTimeout(res, 3000, 'bar')), new Promise ((res, rej) => setTimeout(res, 1000, 'baz')), ])

it would resolve (in three seconds, when the final promise resolves) to the array ['foo', 'bar', 'baz'] .它会解析(在三秒钟内,当最终的 promise 解析时)到数组['foo', 'bar', 'baz'] And if we added this:如果我们添加这个:

 new Promise ((res, rej) => setTimeout(rej, 2000, 'qux')),

then it would reject (in two seconds, when the first promise is rejected) with the value 'qux' .然后它将拒绝(在两秒钟内,当第一个 promise 被拒绝时)值为'qux'

Promise.all is a real convenience. Promise.all真的很方便。 We now have a Promise that when everything succeeds, gives us an array of breeds.我们现在有一个 Promise,当一切顺利时,它会为我们提供一系列品种。

  .then (breeds => breeds.map(
    breed => `<img src="${breed .url}" title="${breed .name}" width="100"/>`
  ).join (''))
  .then (pics => document .getElementById ('cats') .innerHTML = pics)

This just creates an HTML string and inserts it into the document.这只是创建一个 HTML 字符串并将其插入到文档中。 You would need to consult your React documentation to find the equivalent asynchronous processing with React.您需要查阅您的 React 文档以找到与 React 等效的异步处理。 But it shouldn't be hard.但这应该不难。

  .catch (err => console .log (`Error: ${err}`))

Finally, if there's an error and one of the Promises can't be resolved, the rejection reason will come here.最后,如果出现错误并且其中一个 Promise 无法解决,那么拒绝原因就会出现在这里。 You would presumably also handle this with a React technique.您可能还会使用 React 技术来处理这个问题。

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

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