2

I have been trying to get the images from this API to append to the page by mapping through them, but I keep getting one of two error messages saying 'undefined.map is not a function' or 'getBirds.map is not a function.'I've tried leaving the array of objects as it is and setting state to an object and an array(at separate times) but that didn't work. I've also tried using Object.key, Object.values, and Object.entries(each at separate times) to turn the array of objects into an array and then map through my variable and through getBirds(again separately) but those attempts also failed. I have attached three of my attempts below. Can someone help me understand where I've gone wrong?

// Attempt 1
import {useState, useEffect} from 'react'
import axios from 'axios'

function Birds(props) {
   const [getBirds, setGetBirds] = useState({})
   const {image} = props

   useEffect(() => {
      async function fetchBirds() {
         const URL = `https://audubon-society-api.herokuapp.com/birds`
         try {
            const res = await axios.get(URL)
            console.log(res.data)
            setGetBirds(res.data)
         } catch (error) {
            console.log(error)
         }
      }
      fetchBirds()
   }, [])
   
   if (!getBirds) return <h3>Loading...</h3>

   return (
      <div>
         <img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
      </div>
   )
}

export default Birds

// Attempt 2
import {useState, useEffect} from 'react'
import axios from 'axios'

function Birds(props) {
   const [getBirds, setGetBirds] = useState([])
   const {image} = props

   useEffect(() => {
      async function fetchBirds() {
         const URL = `https://audubon-society-api.herokuapp.com/birds`
         try {
            const res = await axios.get(URL)
            console.log(res.data)
            setGetBirds(res.data)
         } catch (error) {
            console.log(error)
         }
      }
      fetchBirds()
   }, [])
   
   if (!getBirds) return <h3>Loading...</h3>

   return (
      <div>
         <img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
      </div>
   )
}

export default Birds

// Attempt 3
import {useState, useEffect} from 'react'
import axios from 'axios'

function Birds(props) {
   const [getBirds, setGetBirds] = useState({})
   const {image} = props

   useEffect(() => {
      async function fetchBirds() {
         const URL = `https://audubon-society-api.herokuapp.com/birds`
         try {
            const res = await axios.get(URL)
            console.log(res.data)
            setGetBirds(res.data)
         } catch (error) {
            console.log(error)
         }
      }
      fetchBirds()
   }, [])

   const birds = Object.entries(getBirds)

   birds.forEach(([key, value]) => {
      console.log(key, value)
   })
   
   if (!getBirds) return <h3>Loading...</h3>

   return (
      <div>
         <img src={birds.map(image)} alt={birds.map(image)}></img>
      </div>
   )
}

export default Birds
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

2 Answers 2

3

You would need to initialize your state with an array, so the map function won't get errors, and correct the way you map it:

  • Initialize state with an array:
const [getBirds, setGetBirds] = useState([]);
  • Map it:
return (
  <div>
    {getBirds.map((bird) => (
      <img src={bird.image} alt={bird.image}></img>
    ))}
  </div>
);
  • Also, check your array with length, because [] or {} both equal to true.
if (!getBirds.length) return <h3>Loading...</h3>;

console.log(!![]);
console.log(!!{});

console.log(!![].length)

The completed solution:

import { useState, useEffect } from "react";
import axios from "axios";

function Birds(props) {
  const [getBirds, setGetBirds] = useState([]);

  useEffect(() => {
    async function fetchBirds() {
      const URL = 'https://audubon-society-api.herokuapp.com/birds';
      try {
        const res = await axios.get(URL);
        console.log(res.data);
        setGetBirds(res.data);
      } catch (error) {
        console.log(error);
      }
    }
    fetchBirds();
  }, []);

  if (!getBirds.length) return <h3>Loading...</h3>;

  return (
    <div>
      {getBirds.map((bird) => (
        <img src={bird.image} alt={bird.image}></img>
      ))}
    </div>
  );
}

export default Birds;

Working Example:

Edit focused-hertz-ux7im

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

2 Comments

It worked, thanks! I did set it to an array in my second attempt, but was mapping inside my img src and alt attribute. Why did it work as an arrow function but not how I did it?
JS Array.map return an array, but the src and alt receive a single string or the URL and name respectively. You will need to map it outside the tag and give src, alt actual string inside the array.
0

Your init state of birds and setBirds should be an array [] not an object {}, also you don't need:

const birds = Object.entries(getBirds). fetch return array of birds already.

<img src={birds.map(image)} alt={birds.map(image)}></img> is wrong, the array loop map should render an image for each bird.

Below code will run for your need:

    import React, {useState, useEffect} from "react";
    import axios from 'axios';
    
    function Birds(props) {
      //- const [getBirds, setGetBirds] = useState([])
      //- const {image} = props
      // +
      const [birds, setGetBirds] = useState([])
      useEffect(() => {
         async function fetchBirds() {
            const URL = `https://audubon-society-api.herokuapp.com/birds`
            try {
               const res = await axios.get(URL)
               console.log(res.data)
               setGetBirds(res.data)
            } catch (error) {
               console.log(error)
            }
         }
         fetchBirds()
      }, [])
    
      // - const birds = Object.entries(getBirds)
    
      // - birds.forEach(([key, value]) => {
      // -   console.log(key, value)
      // - })
      
      // - if (!getBirds) return <h3>Loading...</h3>
      if (!birds) return <h3>Loading...</h3>
      return (
         <div>
          {/* <img src={birds.map(image)} alt={birds.map(image)}></img> */} 
           {birds.map((item, index) => 
            <img src={item.image} alt={index}></img>
            )}
         </div>
      )
    }
    
    export default Birds

Comments

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.