Learning React: Trouble understanding why my component won't render/ API/KEYS - reactjs

I am fairly new to learning React and need some help.
I want to create a meal finder app using the MealDb API, my problem is it won't render.
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;
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}.
EDIT: I did try to write
{meals.map(meal=>(
))}
to no avail, unfortunately. It gives me a type error meals.map is not a function.
This is the structure of the API:
{
"meals": [
{
"idMeal": "52772",
"strMeal": "Teriyaki Chicken Casserole",
"strDrinkAlternate": null,
"strCategory": "Chicken"
}
]
}
Edit # 2: This is what I get when I console.log(meals)
console.log(meals)
Thank you to anyone willing to help! Have a good day!

Reason is that you are using
Object.keys(meals).map.
Instead, do this
{meals.map(meal=>(
<MealsItem key={meal.idMeal} meal={meal}/>
))}
EDIT: Also set default value of meals state.
const [meals, setMeals] = useState([]); // dont use useState({})
Otherwise in the initial render before meals are fetched there will be error as we try to map over an object.

Related

React render instagram feeds from data.json

I"m trying to render username comments with likes and a like. please see image on demo.
All of this comes form data.json
I can't find a way to display the json properly on the tags. What am I missing here?
Sorry I'm trying my best here with react as I'm quite a beginner.
demo
my index.js
import React from "react";
import styles from "./styles";
import { getCaptionFromEdges } from "./helpers";
const Posts = (props) => {
const { data } = props;
return (
<img src={data.owner.profile_pic_url} /> // this tag works
<p>{data.owner.node.username}</p> // this tag doesn't work
<hr>
//here I should display all comments with its like.
<p>{data.node.text}</p>// this doesn't work
);
};
export default Posts;
You need to wrap your elements with another element (or a Fragment). Try the following:
import React from "react";
import styles from "./styles";
import { getCaptionFromEdges } from "./helpers";
const Posts = (props) => {
const { data } = props;
return (
<>
<img src={data.owner.profile_pic_url} />
<p>{data.owner.node.username}</p>
<hr />
<p>{data.node.text}</p>
</>
);
};
export default Posts;

why is my component getting rendered once but then failing on refresh

i am working on small react assignment,
following is my component code. So my component is getting rendered once but then it just fails.i'll attach the screenshots too, can some one please explain what is happening?is there an error in the code or is it because of some rate limiting in API i am using?
import React from 'react'
const Menu = ({events}) => {
console.log(events);
return (
<div>
{events.map((event)=>{
return( <div key={event.category}>
<h3>{event.category}</h3>
</div>)
})}
</div>
)
}
export default Menu
code working image
error on same code pic
parent component code
import React,{useState,useEffect} from 'react';
import './App.css';
import Menu from './components/Menu';
function App() {
const [isLoading,setISLoading] = useState(true);
const[events,setEvents] = useState()
const getEvents = async()=>{
const response = await fetch('https://allevents.s3.amazonaws.com/tests/categories.json');
const eventsData =await response.json()
setISLoading(false);
setEvents(eventsData);
}
useEffect(()=>getEvents(),[]);
return (
isLoading?<h1>Loading...</h1>:<Menu events = {events}/>
);
}
export default App;
May be the parent component of Menu which is supplying events is not using any loading state. So when the component is mounted and starts making ajax calls, events is undefined. You need to put a condition over there like this:
import React from 'react'
const Menu = ({events}) => {
console.log(events);
return events ? (
<div>
{events.map((event)=>{
return( <div key={event.category}>
<h3>{event.category}</h3>
</div>)
})}
</div>
) : null
}
export default Menu

why can't I pass an array as props from one js to another?

I have three js file App.js, PeopleData.js, PeopleDetail.js
App.js is the main application containing all other components including PeopleDetail.js
PeopleData.js contains JSON array data that is imported to App.js
PeopleDetail.js is the components displaying the data from PeopleData.js
But I was trying to pass the JSON from App.js to PeopleDetail.js as props, it would change it to an object instead of an array.
This is App.js
import React from "react";
import PeopleData from "./PeopleData";
import PeopleDetail from "./PeopleDetail";
function App() {
console.log(PeopleData)
return (
<div>
<PeopleDetail props={PeopleData} />
</div>
);
}
export default App;
Even I was using the console.log to make sure the JSON I am passing is an array, it would still end up being an object in the PeopleDetail.js and I can't use the map function on it.
This is PeopleDetail.js
import React from "react";
function PeopleDetail(props) {
console.log(props);
const list = props.map((people) => (
<div key={people.id}>
<li>
<h1>{people.name}</h1>
</li>
</div>
));
return <div>{list}</div>;
}
export default PeopleDetail;
This is PeopleDetail.js
const peopleData = [
{
name: "Peter",
id: 1,
},
{
name: "Tom",
id: 2,
},
];
export default peopleData;
Any help would be appreciated!!! Thank you so much!
You need to pull peopleData out of your props object. You can use a destructuring approach like below or just change your map to props.people.map(...). I recommend naming your attribute something other than props, so you don't get confused with the normal props object. Right now you have an props object with a props array.
import React from "react";
import PeopleData from "./PeopleData";
import PeopleDetail from "./PeopleDetail";
function App() {
console.log(PeopleData)
return (
<div>
<PeopleDetail people={PeopleData} />
</div>
);
}
export default App;
import React from "react";
function PeopleDetail({ people }) {
console.log(people);
const list = people.map((person) => (
<div key={person.id}>
<li>
<h1>{person.name}</h1>
</li>
</div>
));
return <div>{list}</div>;
}
export default PeopleDetail;

Is there a way in React Javascript to pass props and use it in external import?

I want to pass props from one component to another, and use it in the second one for an import above the component declaration
This is for using the same component, with no need to create it 4 times, every time with another SVG.
I'm using React, Javascript, Webpack, babel.
I'm also using svgr/webpack to create a component from an SVG picture, and it's crucial for me to use SVG not < img >.
import React from 'react';
import RightNavItem from './right_nav_item';
const RightNav = ({navitems}) => {
const rightNavItems = navitems.map( (item) => {
return <RightNavItem name={ item }/>
});
return(
<div className="rightnav">
{rightNavItems}
</div>
);
};
.
export default RightNav;
import React from 'react';
const RightNavItem = ({ name }) => {
const svgpath = `../../../../resources/img/navbar/${name}.svg`;
return(
<div>
<img src={ svgpath } style={{height: '25px'}}/>
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
And I want to achieve being able to do this:
import React from 'react';
import SvgPicture from '../../../../resources/img/navbar/{name}.svg';
const RightNavItem = ({ name }) => {
return(
<div>
<SvgPicture />
<span>{ name }</span>
</div>
);
};
export default RightNavItem;
.
Ok so I went back and implemented the whole thing on my local app to get exactly what you need. I am editing my original answer. Hope this solves your issue.
The parent:
import React from 'react';
import { ReactComponent as svg } from 'assets/img/free_sample.svg';
import RightNavItem from './RightNavItem';
const LOGOS = [
{ name: 'home', svg },
{ name: 'home', svg },
];
const RightNav = () => (
<div>
{LOGOS.map(logo => (
<RightNavItem name={logo.name}>
<logo.svg />
</RightNavItem>
))}
</div>
);
export default RightNav;
The child:
import React from 'react';
const RightNavItem = ({ name, children }) => (
<div>
{children}
<span>{name}</span>
</div>
);
export default RightNavItem;
You don't need to import the svg as I did, if you are able to use svg as a component in your webpack config then continue to do what you were doing before.
I managed to do it in a kind of ugly way, but it works.
The problem is if I have more than 4 items, then using it without the map() function can be really annoying.
I used {props.children}, and instead of using map(), I added the 4 times, each with different 'SVG' component child and different props 'name', that way the component only gets initialized at the RightNavItem level.
IF SOMEONE KNOWS how can I use this with the map() function, It'll help a lot!
Thanks to everyone who helped!
For example:
const RightNav = (props) => {
return(
<div className = "rightnav">
<RightNavItem name = {home}>
<HomeSVG />
</RightNavItem>
<RightNavItem name = {profile}>
<ProfileSVG />
</RightNavItem>
.
.
.
</div>
);
};
And in the RightNavItem:
const RightNavItem = (props) => {
return(
<div>
{props.children}
<span>{ props.name }</span>
</div>
);
};

Reactjs: Axios.post call returns array object from database - need to map to other component

I've been struggling with this for a couple days, and any help would be appreciated.
In this component, I have tried to do an HTTP call to my server and database. After parsing the response, using JSON.parse, I am getting back a correctly formed JSON object. I then want to map through that object and for each return a new component (called HistoryItem).
The code below attempts to do this by placing the object into the component state, but it is causing an infinite refresh loop. Previously I had tried a functional component.
The original iteration of this component did work. But it pulled a static JSON object from my client side files. Therefore, I am confident code works without the http call.
It seems to me I am doing something wrong with the async, which is disallowing the JSON object received asynchronously from being rendered.
Below is the main component. Note the component imports the username from redux. This feeds the HTTP call, so that it retrieves only records associated with the logged in user. Again, everything looks fine on the server/database end...
import React, {Component} from 'react';
import style from './history.css';
import HistoryItem from './HistoryItem/historyItem';
import data from '../../config/fakermyhistory.json';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import axios from 'axios';
class History extends Component {
constructor(props) {
super(props);
this.state = {
compiledList:[]
}
}
getData(){
this.state.compiledList.map((call, i) => {
const shaded = (call.rated) ? 'lightgrey' : 'white';
console.log("shaded", shaded);
return(
<Link to={`/reviewpage/${call._id}`} key={call._id}
style={{ textDecoration: 'none', color:'lightgrey'}}>
<div style={{backgroundColor:shaded}}>
<hr/>
<HistoryItem call={call}/>
</div>
</Link>
)
})
}
render(){
axios.post('/api/history', {username: this.props.username})
.then((res) => {
const array = JSON.parse(res.request.response);
this.setState({compiledList: array})
console.log("res", array);}
).catch((err) => console.log("err", err));
return (
<div className={style.container}>
<div className={style.historyHeader}>
<div className={style.historyHeaderText}>
Your Call History
</div>
</div>
<div className={style.historyList}>
{this.getData()};
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
username:state.auth.username
};
}
export default connect(mapStateToProps, null)(History);
Thanks in advance if you can help.
Here is another version using it as a functional component. Also doesn't render (although no errors on this one)
import React, {Component} from 'react';
import style from './history.css';
import HistoryItem from './HistoryItem/historyItem';
import data from '../../config/fakermyhistory.json';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import axios from 'axios';
const History =(props)=> {
const getData=(props)=>{
console.log("props", props);
axios.post('/api/history', {username: props.username})
.then((res) => {
const array = JSON.parse(res.request.response);
console.log("array", array);
array.map((call, i) => {
const shaded = (call.rated) ? 'lightgrey' : 'white';
console.log("shaded", shaded);
return(
<Link to={`/reviewpage/${call._id}`} key={call._id}
style={{ textDecoration: 'none', color:'lightgrey'}}>
<div style={{backgroundColor:shaded}}>
<hr/>
<HistoryItem call={call}/>
</div>
</Link>
)
})
}
).catch((err) => console.log("err", err));
}
return (
<div className={style.container}>
<div className={style.historyHeader}>
<div className={style.historyHeaderText}>
Your Call History
</div>
</div>
<div className={style.historyList}>
{getData(props)};
</div>
</div>
)
}
const mapStateToProps = state => {
return {
username:state.auth.username
};
}
export default connect(mapStateToProps, null)(History);
Instead of calling axios in render function, try to invoke it from componentDidMount.
This will help you prevent the infinite loop.
To return the components rendered within the map function, it was necessary to add a "return" command before the map function was called:
return array.map((call, i) => {...

Resources