0

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)

4
  • What is data2 at this point const data2 = await response2.data2 ? Btw if it is a list then you want to index it like this.state.data2[index]. Commented Nov 17, 2020 at 0:07
  • this is a data I exctract fro the second API.I tried that way, too. It returned the same error Commented Nov 17, 2020 at 0:26
  • Why do you think there's a field on response2 called data2? It definitely has a data. Commented Nov 17, 2020 at 0:33
  • Change response2.data2 to response2.data Commented Nov 18, 2020 at 20:01

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

thanks for this, unfortunately it still doesn't work
based on my purpose you would need to adapt your cat mapping to something like this.state.cats.map(cat => <Cat image={cat.url} cat={cat} key={cat.id} />). does it throw the same error? you could put it in a sandbox and share it here. meanwhile, I suggest to console.log your state on componentDidUpdate to understand it better or use some react chrome extensions.
I was playing around with it some more and eliminated one issue, still unable to map rest of the images. {cat.url} in my case needs some [id] I guess.
anyone, please?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.