Al_Milligan
Al_Milligan

Reputation: 119

Mapping data from different API in one component in react.js?

I'm working on a challenge. My task is to use two APIS: https://api.thecatapi.com/v1/images/search?breed_id=abys and https://api.thecatapi.com/v1/breeds

to display image and description. So far I've got:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'

const Cat = ({ cat: { name, weight, life_span, description } }, { image: url }) => {
  return (
    <div className='cat'>
      <div className='cat_wrapper'>
        <img src={url} alt="catimage" />
      </div>
      <h3 className='cat_breed'>{name.toUpperCase()}</h3>
      <div className='country_text'>

        <span>Weight:{weight.metric}</span>
        <span>Lifespan: {life_span}</span>
        <div>Description: {description}</div>


      </div>
    </div>
  )
}

class App extends Component {
  state = {
    data: [],
    data2: []
  }

  componentDidMount() {
    this.fetchCatData()
  }
  fetchCatData = async () => {
    const url = 'https://api.thecatapi.com/v1/breeds'
    const images = 'https://api.thecatapi.com/v1/images/search?breed_id=abys'
    try {
      const response = await axios.get(url)
      const response2 = await axios.get(images)
      const data = await response.data
      const data2 = await response2.data2
      this.setState({
        data, data2
      })
    } catch (error) {
      console.log(error)
    }
  }

  render() {

    return (
      <div className='App'>

        <div>
          <p>There are {this.state.data.length} cats in the api</p>
          <div className='wrapper'>
            {this.state.data.map((cat, image) => (
              <Cat image={this.state.data2.image} cat={cat} key={cat.id} />
            ))}
          </div>
        </div>
      </div>
    )
  }
}

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)

But it returns an error

× TypeError: Cannot read property 'image' of undefined

How to fix it?

-=EDITED=-

so, now it reads the 'url' key, earlier it was undefined. I managed to display a hardcoded image of a cat, now I wonder how to map all the different images to its proper contents.

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'

const Cat = ({ cat: { name, weight, life_span, description, url }}) => {
  return (
    <div className='cat'>
      <div className='cat_wrapper'>
        <img alt="catimage" src={url} width="50" height="30"/>
      </div>
      <h3 className='cat_breed'>{name.toUpperCase()}</h3>
      <div className='country_text'>

        <span>Weight:{weight.metric}</span>
        <span>Lifespan: {life_span}</span>
        <div>Description: {description}</div>


      </div>
    </div>
  )
}

class App extends Component {
  state = {
    data: [],
    data2: []
  }

  componentDidMount() {
    this.fetchCatData()
  }
  fetchCatData = async () => {
    const url = 'https://api.thecatapi.com/v1/breeds'
    const images = 'https://api.thecatapi.com/v1/images/search?breed_id=abys'
    try {
      const response = await axios.get(url)
      const response2 = await axios.get(images)
      const data = await response.data
      const data2 = await response2.data
      this.setState({
        data, data2
      })
    } catch (error) {
      console.log(error)
    }
  }

  render() {

    return (
      <div className='App'>

        <div>
          <p>There are {this.state.data.length} cats in the api</p>
          <div className='wrapper'>
            {this.state.data.map((cat) => {
                  
               
                  cat.url = this.state.data2[0].url;
               return    <Cat cat={cat} key={cat.id}/>

            }

             
            )}
          </div>
        </div>
      </div>
    )
  }
}

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)

Upvotes: 0

Views: 280

Answers (1)

buzatto
buzatto

Reputation: 10382

you better first fetch all data cats then, for each cat make a request to fetch its correspondent image for that cat and add an image or url attribute like:

  fetchCatData = async () => {
    const catsDataUrl = 'https://api.thecatapi.com/v1/breeds'
    const searchImgUrl = 'https://api.thecatapi.com/v1/images/search?breed_id='
    try {
      const catsResponse = await axios.get(catsDataUrl)
      const cats = catsResponse.data
      
      await Promise.allSettled(cats.map(async (cat) => {
        const response = await axios.get(`${searchImgUrl}${cat.id}`)
        cat.url = response.data[0].url // data is an array with a single object        
      }))

      this.setState({cats}) // I changed state name for cats to be more semantic
      
    } catch (error) {
      console.log(error)
    }
  }

now you don't need data2, you already have url. fwiw, your error is given the fact that data2 is an array and not an object with image attribute.

Upvotes: 1

Related Questions