ReactJS - How to setSate in a map function - javascript

I got an object with another object inside and I need to render the DOM like this:
Event
Tickets 1
Lot 1
Name
Lot 2
Name
Lot 3
Name
Ticket 2
Lot 1
Name
Lot 2
Name
{ this.state.dates.map((date, i) =>
<span className="event-date" key={i}>
{ date.date }
</span>
this.setState({tickets: date.tickets}) //Here, I need to update an object to map in another place
)}
./src/pages/Event/Event.js
Syntax error: D:/YEAPPS/marketplace/pwa/src/pages/Event/Event.js: Unexpected token, expected , (54:32)
Click to see the object

Do not call setState from within a render function, it will cause unnecessary rerenders, i.e. do not update the state with your tickets here
Set up your state within the component using other lifecycle hooks, e.g. componentDidMount, then simply map over your this.state.tickets.map(), or as you are already doing, this.state.dates.map()

I was able to solve like this:
{
this.state.tickets.map((ticket, i) => (
<div key={i}>
<div className="row">
<div className="col">
<h3 className="ticket-name">{ ticket.name }</h3>
</div>
</div>
{ticket.lot.map((lot, j) =>
<div className="row" key={i}>
<div className="col-8">
<h5 className="lot-name">{ lot.name }</h5>
<h6 className="lot-price">
R$ { lot.price.replace('.', ',') } <br />
<small>(R$ { lot.price.replace('.', ',') } + R$ { lot.price_tax.replace('.', ',') })</small>
</h6>
</div>
<div className="col-4">
<ChooseQuantity />
</div>
</div>
)}
<hr />
</div>
)
)
}

Related

Can't access nested Objects JSON in React JSX

I can access JSON objects in the results here
useEffect(() => {
fetch(`/user/${userid}`,{
method:'get',
headers:{
"Authorization":`Bearer ${localStorage.getItem('token')}`}
}).then(res=>res.json())
.then(result=>{
console.log(result.user.name) //I can access JSON objects in the results here//
setProfile(result)
})
.catch(err=>console.log(err))
}, [])
I can access JSON while Changing State but it throws errors like UserProfile.user is undefined,UserProfile.posts.length is undefined when rendering JSX. TO access the nested Data I have tried creating more state Variables. It works but the code have become long. I looked for solution in various website but could not find. Any one help me.
return (
<>
{
UserProfile?<div style={{maxWidth:'800px',margin:"0px auto"}}>
<div style={{
display:"flex",
justifyContent:'space-around',
margin:"18px 0px"
}}>
<div>
<img style={{borderBottom:"1px solid grey",width:"160px",height:"160px",borderRadius:"80px"}} src="https://images.unsplash.com/photo-1569466896818-335b1bedfcce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60"/>
</div>
<div>
<h4>{UserProfile?UserProfile.user.name:"Loading..."}</h4>
<div style={{display:"flex",justifyContent:"space-between",width:"100%"}}>
<h6>{UserProfile.posts.length} posts </h6>
<button onClick={()=>followUser()} className="btn waves-effect waves-light #64b5f6 blue lighten-2" type="button" name="action">Follow</button>
</div>
</div>
</div>
<div className="gallery">
{
UserProfile.posts.map((item)=>{
return(
<img className="item" key={item._id} src={item.photo}/>
)
})
}
</div>
</div>:
<h1>Loading:</h1>
}
</>
)
export default Profile
Based on the code and your inputs, the problem may be because you are trying to access the variables before they are accessible.
As you are making async API call in useEffect() it may take some time before you get data. But, as you are accessing the data before you even get it errors like 'UserProfile.user is undefined', 'UserProfile.posts.length' is undefined will occur'
To avoid such errors make sure you add a check as shown below
<>
{
UserProfile &&
<div style={{maxWidth:'800px',margin:"0px auto"}}>
<div style={{
display:"flex",
justifyContent:'space-around',
margin:"18px 0px"
}}>
<div>
<img style={{borderBottom:"1px solid grey",width:"160px",height:"160px",borderRadius:"80px"}} src="https://images.unsplash.com/photo-1569466896818-335b1bedfcce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60"/>
</div>
<div>
/* -----> modify it here*/ <h4>{UserProfile ? UserProfile.user && UserProfile.user.name:"Loading..."}</h4>
<div style={{display:"flex",justifyContent:"space-between",width:"100%"}}>
/* -----> modify it here*/ <h6>{UserProfile && UserProfile.posts && UserProfile.posts.length} posts </h6>
<button onClick={()=>followUser()} className="btn waves-effect waves-light #64b5f6 blue lighten-2" type="button" name="action">Follow</button>
</div>
</div>
</div>
<div className="gallery">
{
UserProfile.posts.map((item)=>{
return(
<img className="item" key={item._id} src={item.photo}/>
)
})
}
</div>
</div>
:
<h1>Loading:</h1>
}
</>
I got the solution for this. I got rid of this problem by setting initial state to null. Thank You for answering my query.
//my initial useState declaration//
const [userProfile,setUserProfile]=useState([])
//Solution//
const [userProfile,setUserProfile]=useState(null)
I am new to react so I got into problem.

Cards inside the grid-container: each child in a list should have a unique "key" prop

What I`m doing wrong?It also says: "Check the render method of Card" , which is here:
<div className="grid-container">
{pokemonData.map((pokemon, i) => {
console.log(pokemon.id) // unique numbers are here
return <Card key={pokemon.id} pokemon={pokemon} />
})}
</div>
Card component itself:
function Card({ pokemon }) {
return (
<div className="card">
<div className="card__image">
<img src={pokemon.sprites.front_default} alt="Pokemon" />
</div>
<div className="card__name">
{pokemon.name}
</div>
<div className="card__types">
{
pokemon.types.map(type => {
return (
<div className="card__type" style={{backgroundColor: typeColors[type.type.name]}}>
{type.type.name}
</div>
)
})
}
</div>
<div className="card__info">
<div className="card__data card__data--weight">
<p className="title">Weight:</p>
<p>{pokemon.weight}</p>
</div>
<div className="card__data card__data--height">
<p className="title">Height:</p>
<p>{pokemon.height}</p>
</div>
<div className="card__data card__data--ability">
<p className="title">Abilities:</p>
{/* {console.log(pokemon.abilities)} Temporary for dev puprose */}
{pokemon.abilities.map(ability => <p>{ability.ability.name}</p>
)}
</div>
</div>
</div>
);
}
export default Card;
You can use the index of the array may be your data is having some kind of duplicate. It is recommended that you pass a key prop whenever you are returning a list.
<div className="grid-container">
{pokemonData.map((pokemon, i) => {
console.log(pokemon.id) // unique numbers are here
return <Card key={i} pokemon={pokemon} />
})}
</div>
Equally, check this segment of card components.
{
pokemon.types.map((type,i) => {
return (
<div key={i} className="card__type" style={{backgroundColor:
typeColors[type.type.name]}}>
{type.type.name}
/div>
)
})
}
And
<div className="card__data card__data--ability">
<p className="title">Abilities:</p>
{/* {console.log(pokemon.abilities)} }
{pokemon.abilities.map((ability, i) => <p key={i}>{ability.ability.name}
</p>
)}
</div>
Previous answer will solve your problem. However, for your info, I would also like to add here.
For React a key attribute is like an identity of a node/element/tag which helps React to identify each item in the list and apply reconciliation correctlyon each item. Without a key React will render your component but may cause issue when you re-order your list.
React recommends to use id of the data instead of index number. However, if your list does not re-orders/ sorts or do not have id then you can use index.
You can read more here:
https://reactjs.org/docs/lists-and-keys.html
Change this:
<div className="card__types">
{
pokemon.types.map(type => {
return (
<div className="card__type"
style={{backgroundColor:typeColors[type.type.name]}}
>
{type.type.name}
</div>
)
})
}
</div>
to:
<div className="card__types">
{
pokemon.types.map((type, key) => {
return (
<div key={key} className="card__type"
style={{backgroundColor:typeColors[type.type.name]}}
>
{type.type.name}
</div>
)
})
}
</div>
and:
{pokemon.abilities.map(ability => <p>{ability.ability.name}</p>
to:
{pokemon.abilities.map((ability,key) => <p key={key} >{ability.ability.name}</p>

Passing of Props in ReactJS

Following is my JSX code in a React component which is working fine and currently in use, but in most of React blog posts they are also de structuring an object. My query is - do we have any extra benefit of modifying the code to Version 2 or this is just fine.
First Version (currently in use) -
const CartItems = ({ items }) => items.length ? items.map((x, i) => (
<div key={x.id} className={`cart-item-${i}`}>
<div className="card ">
<div className="cart-item-img">
<img src={x.url} alt={x.altText} className="img" />
</div>
<div className="cart-item-desc">
<h3 className="title">{x.title}</h3>
<p className="text">{x.shortDesc}</p>
</div>
<div className="cart-item-action">
<button className="add">+</button>
<button className="subtract">-</button>
<button className="remove">X</button>
</div>
</div>
</div>)) : []
2nd Version -
const CartItems = ({ items }) => items.length ? items.map((x, i) => {
const {
id,
url,
altText,
title,
shortDesc
} = x;
return (
<div key={id} className={`cart-item-${i}`}>
<div className="card ">
<div className="cart-item-img">
<img src={url} alt={altText} className="img" />
</div>
<div className="cart-item-desc">
<h3 className="title">{title}</h3>
<p className="text">{shortDesc}</p>
</div>
<div className="cart-item-action">
<button className="add">+</button>
<button className="subtract">-</button>
<button className="remove">X</button>
</div>
</div>
</div>)
}) : []
The benefits are mostly aesthetic and subjective, so if you prefer the first one, more power to you and nothing that says you need to change it.
My personal view on the two snippets you posted: I tend to avoid direct returns from arrow functions because I'll oftentimes need to add a log or something else and having to convert back and forth eventually wears on you. This has little to do with the destructuring though, other than destructuring forces you to have a function body and explicit return.

Pure render and button onClick inline setStates

When talking about pure render methods in React and how setting state inside of render is a serious anti-pattern, how strictly we are speaking about this? I get that if I do a setState inside a render function, which doesn't require any user input, it can create infinite loops and re-renders.
What I don't understand is, does this rule apply also when writing a page element that receives input from user? I mean, what's the difference between these two ways to write a simple click-handler for a button:
render() {
return(
<div className="container-fluid info-modal">
<div className="row">
<div className="col col-12">
<InfoModal active={this.state.modalActive}>
<h2>Fine dining recipes</h2>
<p>Here you can publish your fine dining recipes. Don't forget to
include every ingredient needed!</p>
<Button
title="ok"
onClick={() => {
this.setState({ modalActive: false })
}}
/>
</InfoModal>
</div>
</div>
</div>
)
}
vs.
render() {
return(
<div className="container-fluid info-modal">
<div className="row">
<div className="col col-12">
<InfoModal active={this.state.modalActive}>
<h2>Fine dining recipes</h2>
<p>Here you can publish your fine dining recipes. Don't forget to
include every ingredient needed!</p>
<Button
title="ok"
onClick={() => {
this.closeModal()
}}
/>
</InfoModal>
</div>
</div>
</div>
)
}
I get that for a more complicated components, probably the right way to do this would be to use a click-handler which this class receives as an property, but for simple use cases, are there really some concrete harm in setting the state inside an inline click-handler?
When docs say that you must not setState within render it means that that the setState function must not be called as soon as we render and not that setState cannot be written inside render
so when you write
<Button
title="ok"
onClick={() => {
this.setState({ modalActive: false })
}}
/>
You are not actually calling setState in render but you are calling it on click action of button which are 2 different things
So whether you write
<Button
title="ok"
onClick={() => {
this.closeModal()
}}
/>
or
<Button
title="ok"
onClick={() => {
this.setState({ modalActive: false })
}}
/>
are equivalent if you just setState inside closeModal
A call that will not be accepted is below
<Button
title="ok"
onClick={this.setState({ modalActive: false })}
/>

Cannot Render Nested Maps In ReactJS

I am trying to nest maps to render an array within an object
My Cards Component Render Method (Not Nested, Working):
render() {
return (
<div class="mediator-container">
{this.state.routeList.map(
(route, index) =>
<Card busName={this.state.routeList[index].$.tag} />
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}
My Cards Component Render Method (Nesteing, Not Working!!):
render() {
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
{
{
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
<Card busName={busDir} />;
});
}
}
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}
busTitleToDirectionName(int) returns an array of Strings
My Card Subcomponent's render method:
render() {
// Logging to see if render method is called
console.log("Ran");
return (
<div className="card">
<div className="card-header">
<div className="bus-name">
<p>{this.props.busName}</p>
</div>
</div>
</div>
);
}
How it looks like without nesting when it does work (Not enough reputation to post images so here are the links):
https://i.gyazo.com/66414925d60701a316b9f6325c834c12.png
I also log in the Card subcomponent so that we know that the Card component was ran and it logs that it did get called without nesting
https://i.gyazo.com/fb136e555bb3df7497fe9894273bf4d3.png
When nesting, nothing renders and the Card subcomponent isn't being called as there is no logging of it
https://i.gyazo.com/38df248525863b1cf5077d4064b0a15c.png
https://i.gyazo.com/d6bb4fb413dfc9f683b44c88cce04b8a.png
You can try below code in your nested case. In the nesting of map you have to wrap your nested map within a container. Here I use React.Fragment (<> ) as a container.
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
<>
{
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
<Card busName={busDir} />;
});
}
</>
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
Hope it will help you!!
Thanks, Prahbat Kumar, but I figured out the issue. I had to return the subcomponent from the nested map here is the code:
render() {
return (
<div class="mediator-container">
{this.state.routeList.map((route, index) =>
this.busTitleToDirectionName(this.state.routeList[index].$.tag).map(busDir => {
return <Card busName={busDir} />
})
)}
<span class="loader">
<span class="loader-inner"></span>
</span>
</div>
);
}

Categories

Resources