skip to Main Content

I am trying to iterate over the response I get from an API using the map() method but I keep getting the error ‘drinks.map is not a function’. Below is my code

Drinks

import DrinksList from "@/components/DrinksList";

const url = "https://www.thecocktaildb.com/api/json/v1/1/search.php?f=a";

const fetchDrinks = async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  const response = await fetch(url);

  // throw error
  if (!response.ok) {
    throw new Error("Failed to fetch");
  }

  const data = await response.json();
  return data;
};

const Drinks = async () => {
  const data = await fetchDrinks();

  return (
    <div>
      <DrinksList drinks={data} />
    </div>
  );
};
export default Drinks;

I have tried checking if I had any typos.

2

Answers


  1. The basic issue is that the fetched data is an object with a drinks property that is the array you would like to render.

    {
      "drinks": [
        {
          "idDrink": "17222",
          "strDrink": "A1",
          "strDrinkAlternate": null,
          ...
          "strImageAttribution": null,
          "strCreativeCommonsConfirmed": "No",
          "dateModified": "2017-09-07 21:42:09"
        },
        {
          "idDrink": "13501",
          "strDrink": "ABC",
          "strDrinkAlternate": null,
          ...
          "strImageAttribution": null,
          "strCreativeCommonsConfirmed": "No",
          "dateModified": "2016-08-31 19:32:08"
        },
        ...
      ]
    }
    

    The code is passing this object as the drinks prop and trying to call a map method that is undefined, e.g. the error message that "map is not a function".

    Additionally, React components are synchronous. They cannot be declared async since this implicitly returns a Promise object and this is not valid JSX to be rendered to the DOM.

    Wrap asynchronous logic in a useEffect hook. If you are fetching data then you’ll want some state to hold the data you want to render, e.g. map to valid JSX.

    const url = "https://www.thecocktaildb.com/api/json/v1/1/search.php?f=a";
    
    const fetchDrinks = async () => {
      const response = await fetch(url);
    
      // throw error
      if (!response.ok) {
        throw new Error("Failed to fetch");
      }
    
      const data = await response.json();
      return data;
    };
    
    import { useEffect, useState } from 'react';
    
    const Drinks = () => {
      // state to hold drinks array data, initially empty
      const [drinks, setDrinks] = useState([]);
    
      // fetch drinks data when the component mounts
      useEffect(() => {
        fetchDrinks()
          .then(data => {
            setDrinks(data.drinks); // <-- the array of drinks data
          })
          .catch(error => {
            // handle/ignore any caught errors/rejected Promises
          });
      }, []);
    
      return (
        <div>
          <DrinksList drinks={drinks} /> // <-- pass drinks state array
        </div>
      );
    };
    
    export default Drinks;
    

    It’s a good idea to also provide a default value for the drinks prop in case you render a DrinksList component and pass an undefined drinks prop.

    const DrinksList = ({ drinks = [] }) => {
      ...
    
      return (
        ...
        {drinks.map(drink => (
          <div key={drink.idDrink}>
            ...
          </div>
        ))}
        ...
      );
    };
    
    Login or Signup to reply.
  2. I think you need to iterate drinks like this. Since drinks is an Array whose first element is an object called drinks which again is an Array of Objects.

    So I guess you can use map() function like this on drinks.

    drinks[0]?.drinks.map((item, index) => {
            return <div key={index}>{item.strIngredient2}</div>;
          })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search