同时处理两个API

Posted

技术标签:

【中文标题】同时处理两个API【英文标题】:Handle two APIs at the same time 【发布时间】:2021-01-10 08:25:28 【问题描述】:

我想使用 React 从 API 加载行星数据并将这些数据放入 Material UI 卡组件中。不幸的是,我使用的行星 API 没有照片,我也想在卡片中显示。为此,我正在使用另一个 API。

获取行星数据的API:https://api.le-systeme-solaire.net/rest 获取行星图片的API:https://images-api.nasa.gov/search

我已经弄清楚如何将每个行星组件及其各自的信息渲染到卡片中,但不知道如何将每个 NASA 图像映射到各自的行星:

这是 App.js 组件的代码:

import React, useState, useEffect from 'react';
import './App.css';
import axios from 'axios'
import Block from "./Block"


function App() 
  const [planets, setPlanets] = useState([])
  const [images, setImages] =useState([])
 
  const getPlanets = async()=>
    const data = await fetch
    ('https://api.le-systeme-solaire.net/rest/bodies') //Await for first fetch promise to resolve
    const planets = await(data.json()) //Await for data.json to resolve and save this planets

    setPlanets(planets.bodies.filter 
        (planet =>planet.isPlanet && planet.name.indexOf('1')===-1)) 
  

  const getImages = (name)=>
    axios.get('https://images-api.nasa.gov/search',
      params:
        q: name
      
    )
    .then(res => 
      setImages(images.concat(res.data.collection.items[0].links[0].href)))
   

  console.log(planets)   

  useEffect(()=>
    getPlanets();
  ,[])
  
  useEffect(()=>
    if(planets)
      planets.map(planet =>getImages(planet.englishName))
    
  ,[])

 console.log(images)
 console.log(planets.length)

  return (
    <div className="App">
      planets.map(planet => 
        (<Block 
          key = planet.id
          id = planet.id
          name = planet.englishName
          dateDiscovered = planet.discoveryDate
          mass = planet.mass.massValue
          massExp = planet.mass.massExponent
          image = ???
        />)
      )
    </div>
  );


export default App;

Block.js的代码如下:

import React, useState, useEffect from 'react'
import './Block.css'

import Card, CardContent from '@material-ui/core'
function Block(props)
    return (
        <div>
            <Card>
                <CardContent>
                    <div className = "planetInfo">
                        <h4>props.name</h4>
                        <h5>Mass: props.mass*10^props.massExp kg</h5>
                        <h5>Discovery Date: props.dateDiscovered</h5>
                        <h5>ID: props.id</h5>
                        <img src = props.image/>
                    </div>
                </CardContent>
            </Card>
        </div>
    )



export default Block

我感觉我的结构不正确。任何帮助将不胜感激。

【问题讨论】:

您能否提供一个用于行星和图像的 JSON 示例。 (来自您的 console.logs) 【参考方案1】:

您可以根据数组索引轻松绘制行星和图像 - 假设:

你按顺序检索它们

因为有 8 个行星,所以会有 8 个图像;所以你的Array.map 函数看起来像:

 planets.map((planet,index) => 
  (<Block 
  key = planet.id
  id = planet.id
  name = planet.englishName
  dateDiscovered = planet.discoveryDate
  mass = planet.mass.massValue
  massExp = planet.mass.massExponent
  image = images[index]
  
  />))

【讨论】:

【参考方案2】:

您可以useEffect,当行星阵列发生变化时,您可以遍历行星阵列中行星的名称并调用图像 API。

React.useEffect(() => 
  async function fetchAllImages() 
    const allImages = [];

    for await (const planet of planets) 
      const  data   = axios.get('https://images-api.nasa.gov/search',
      params:
        q: planet.name
      )

      allImages.push(data);
    

    setImages(allImages);
  

  fetchAllImages();
, [planets])

【讨论】:

【参考方案3】:

同时处理多个 API 调用是一个非常困难的问题。我不完全知道这是否可行,但你为什么不改变 const [images, setImages] =useState([]) const [images, setImages] =useState()


  axios.get('https://images-api.nasa.gov/search',
 
 params:
  q: name

 )
 .then(res => setImages(images.concat(res.data.collection.items[0].links[0].href)))
 


  axios.get('https://images-api.nasa.gov/search',
 
 params:
  q: name

 )
 .then(res => 
         images[name] = res.data.collection.items[0].links[0].href));
         setImages(...images);
     )
 

然后您可以循环使用 planets 数组根据植物名称从 images 对象中选择 URL。

【讨论】:

【参考方案4】:

我建议将两个数据获取组合成一个效果挂钩回调。首先获取并过滤真实行星的天体列表,然后获取图像并将行星和图像映射到单个行星数据数组中。现在只需将image=planet.image 传递给Block

function App() 
  const [planets, setPlanets] = useState([]);

  const getPlanets = async () => 
    const data = await fetch("https://api.le-systeme-solaire.net/rest/bodies"); //Await for first fetch promise to resolve
    const planets = await data.json(); //Await for data.json to resolve and save this planets

    // filter planet data
    const filteredPlanets = planets.bodies.filter(
      (planet) => planet.isPlanet && planet.name.indexOf("1") === -1
    );

    // define function here to avoid needing it as dependency
    // return GET request response image value
    const getImages = (name) => 
      return axios
        .get("https://images-api.nasa.gov/search", 
          params: 
            q: name
          
        )
        .then((res) => res.data.collection.items[0].links[0].href);
    ;

    // Fetch and await all images
    const planetImages = await Promise.all(
      filteredPlanets.map((planet) => getImages(planet.englishName))
    );

    // Merge planets and images arrays
    const planetsWithImages = filteredPlanets.map((planet, i) => (
      ...planet,
      image: planetImages[i]
    ));

    setPlanets(planetsWithImages);
  ;

  useEffect(() => 
    getPlanets();
  , []);

  return (
    <div className="App">
      planets.map((planet) => (
        <Block
          key=planet.id
          id=planet.id
          name=planet.englishName
          dateDiscovered=planet.discoveryDate
          mass=planet.mass.massValue
          massExp=planet.mass.massExponent
          image=planet.image // <-- pass planet.image
        />
      ))
    </div>
  );

【讨论】:

【参考方案5】:

我会将图像添加到行星数组中,然后完全删除图像数组。

在这些方面...

    if(planets)
     planets.map(planet =>getImages(planet.englishName))
    
   
  ,[])

您的getImages 函数正在获取图像并将其存储在images

但是由于您已经通过行星进行映射以获取行星名称,因此不要将这些图像存储在单独的变量中,而是将它们返回/存储在行星数组中。

也许是这样的......

if(planets)
    planets.map(planet => 
      const image = getImages(planet.englishName)
      return ...planet, image
    )
  ,[])

所以现在image = ??? 这一行是image = planet.image

也许用一个useEffect 来做这一切......

const getPlanets = async() => 
  const response = await fetch('https://api.le-systeme-solaire.net/rest/bodies')
  const data = await data.json()
  const planets = data.bodies.filter(planet =>planet.isPlanet && planet.name.indexOf('1') === -1)
    
  return planets
      
 
const getImages = async (name) => 
  const response = await axios.get('https://images-api.nasa.gov/search', params:q: name)
  const images = response.concat(res.data.collection.items[0].links[0].href)

  return images



useEffect(()=>
      getPlanets()
        .then(planets => Promise.all(planets.map(async (planet) => 
          const image = await getImages(planet.englishName)
          return  ...planet, image 
        )))
        .then(planets => setPlanets(planets))
  ,[])

*注意我没有测试上面的代码,但希望接近你需要的。

【讨论】:

【参考方案6】:
import React, useState, useEffect from 'react';
import './App.css';
import axios from 'axios'
import Block from "./Block"


function App() 
  const [planets, setPlanets] = useState([])
  const [images, setImages] =useState([])
 

  const getPlanets = async()=>
    const data = await fetch
    ('https://api.le-systeme-solaire.net/rest/bodies') //Await for first fetch promise to resolve
    const planets = await(data.json()) //Await for data.json to resolve and save this planets

    setPlanets(planets.bodies.filter 
        (planet =>planet.isPlanet && planet.name.indexOf('1')===-1)) 
          
 

  useEffect(()=>
      getPlanets();
  ,[])
  

  useEffect(() => 
    async function fetchAllImages() 
      const allImages = [];
  
      for await (const planet of planets) 
        const data  = await axios.get('https://images-api.nasa.gov/search',
        params:
          q: planet.englishName
        )
        const res = await data.data.collection.items[0].links[0].href
        allImages.push(res);
      
  
      setImages(allImages);
     

    
  
    fetchAllImages();
  , [planets])
  console.log(images)

  return (
    
   <div className="App">
     
 planets.map((planet,index) => 
  (<Block 
  key = planet.id
  id = planet.id
  name = planet.englishName
  dateDiscovered = planet.discoveryDate
  mass = planet.mass.massValue
  massExp = planet.mass.massExponent
  image = images[index]
  
  />))
  
   </div>
   
  );


export default App;

【讨论】:

感谢大家的帮助。结果证明这是正确创建图像数组和使用索引进行映射的组合。

以上是关于同时处理两个API的主要内容,如果未能解决你的问题,请参考以下文章

如何在 laravel 中处理同时保存 API 请求?

Elasticsearch批处理操作——bulk API

CountDownLatchCyclicBarrierSemaphore多线程协助操作API

CountDownLatchCyclicBarrierSemaphore多线程协助操作API

异常处理

CompleableFuture