Nested Objects and React

Hello everyone, I’m in a bit of a pickle. I started learning how to program not too long ago and I am trying to build a meal finder app with React. I get typeError meals.map is not a function if I try mapping through meals. I then tried using Object.keys to be able to use map but then nothing renders except a unique item. The API structure really messes up with my head it looks like this:

[https://www.themealdb.com/api/json/v1/1/search.php?s=Arrabiata](http://Api json)

This is an object with a nested array of objects inside. How do you map through this ?
As I said I used Object.keys and at least it logs results in the console but nothing renders. When I search something: I see a unique item with nothing in it. No title, no image. In the console when I inspect the app I read {meals: Array(8)} and “Warning: Each child in a list should have a unique “key” prop.” I thought did write a unique key: key={meal.idMeal}.

Thank you a lot if you can help :slight_smile:

Here is my code, in my app component:

 import React, { useState } from 'react';
 import './App.css';
 import Search from './components/Search';
 import Meals from './components/Meals';
 import axios from 'axios';

 function App() {
 const [meals, setMeals] = useState({});

 const searchMeals = async meals => {
 const res = await axios.get(
  `https://www.themealdb.com/api/json/v1/1/search.php?s=${meals}`
  );
 console.log(res.data);
 setMeals({ meals: res.data });
 };

 return (
 <div className='App'>
  <Search searchMeals={searchMeals}></Search>
  <Meals searchMeals={searchMeals} meals={meals}></Meals>
 </div>
  );
  }

export default App;

I created a component Meals with this inside:

import React from 'react'
import MealsItem from './MealsItem'

const Meals = ({ meals}) => {

 return (


<div >
  {Object.keys(meals).map(meal=>(
    <MealsItem key={meal.idMeal} meal={meal}/>
  ))}
</div>

);

};



 export default Meals

and then for the rendering part I created a MealsItems component:

import React, { Fragment } from 'react';


const MealsItem = ({ meal :{strMealThumb, strMeal,}}) => {
 return (
 <Fragment>
  <div id='result-heading'>
    <h2>Search result for: {strMeal}</h2>
  </div>
  <div className='meal'>
    <img src={strMealThumb} alt={strMeal} />
    <div className='meal-info'>
      <h3>{strMeal}</h3>
    </div>
  </div>
  </Fragment>
 );
};
export default MealsItem;

I get typeError meals.map is not a function if I try mapping through meals

Before going on, I would have tried to find out what type it is. I would have logged typeof meals to the console and see what it is. Also, you should log it, period, just to see what it is. You have to look at and see what you’re getting. That would be step one - everything after that is academic.

Without getting too deep in the weeds, I would assume that what is happening is that you are trying to render Meals before it has any data. I would guess that meals initializes as undefined so you are trying to run a map on that. I often protect against that with something like:

<div >
  { !!meals && Object.keys(meals).map(meal=>(
    <MealsItem key={meal.idMeal} meal={meal}/>
  ))}
</div>

Another possibility is that you are saving the data wrong. I notice that your API response has a prop called “meals” and you are saving it on state as a prop “meals” - you may have an extra level of “meals” there.

Warning: Each child in a list should have a unique “key” prop.” I thought did write a unique key: key={meal.idMeal}

Right, presumably that is a unique ID. But I don’t think your’re getting that since you are mapping through the keys of the meals object instead of mapping through an array of meals. My guess is that is undefined. But log it to the console or inspect that element. This (and the first paragraph) are fundamental troubleshooting skills for a developer. Learn how to use the dev console - it is one of the most powerful tools a developer has.

I don’t think you want to “map” through that object, but select the fields you want to render. You want to map through the array, and in this case it has only one element. It is a weird structure though. You’re going to have to get creative with the ingredients list. (I’m not sure why that wasn’t an array.)

And if you really want help, it would be nice to include a link to a repo or a pen so we can actually work the code.

Hello Kevin! Thanks for your detailed answer and the advice, it really helps! I am still not exactly sure how to access the meals array though . Here is my repo.

First of all, as I said, you have to log this stuff to the console and figure it out. This is a FUNDAMENTAL skill for a developer.

If I go into App.js, and add a console.log:

  const searchMeals = async meals => {
    const res = await axios.get(
      `https://www.themealdb.com/api/json/v1/1/search.php?s=${meals}`
    );
   console.log('here1', res.data);
    setMeals({ meals: res.data });
  };

I see that I am indeed seeing that you are getting back an object with a prop called “meals”:

{
  meals: [
    {
      idMeal: '52771',
      // ...
    }
  ]
}

Then, you saving that object inside another object with a prop called “meals”"

setMeals({ meals: res.data });

So you end up with this:

  const searchMeals = async meals => {
    const res = await axios.get(
      `https://www.themealdb.com/api/json/v1/1/search.php?s=${meals}`
    );
   console.log('here1', res.data);
    setMeals({ meals: res.data });
  };

I see that I am indeed seeing that you are getting back an object with a prop called “meals”:

{
  meals: {
    meals: [
      {
        idMeal: '52771',
        // ...
      }
    ]
  }
}

Then you pass that as a prop called “meals”…

      <Search searchMeals={searchMeals} meals={meals}></Search>

So when it comes into Meals.js, it is nested on props one level deeper, a third level of “meals”. You destructure one level of that, but that’s not enough. Do a console.log in Meals.js (like I suggested in the first place) and see for yourself.

So, how to fix this? Let’s save the data the proper way.

    setMeals(res.data.meals);

“meals” is the array, that’s what we save to state. Now, the way we pass and destructure “meals” makes sense.

When I make this change, the code works.

But please don’t just make this change. Go through the debug steps and see why this is happening. Debugging is one of the most important skills for a dev.

Thanks Kevin. I will go through the debugging with a fresh brain tomorrow because I really feel something has not clicked yet. Have a good night or day!

This is hard stuff. That’s why it pays well. Just keep at it, you’ll get there.