Only getting objects set to true to display in component (React) - javascript

I have two React components.
The first one is just an export of an array that holds multiple objects.
VideoGameList.js
const VideoGameList = [
{
id: 1,
title: "Fire Emblem Engage",
src: vg01,
releaseDate: "01/20/2023",
price: 59.99,
quantity: 1,
inCart: true,
},
{
id: 2,
title: "Pokemon Violet",
src: vg02,
releaseDate: "11/18/2022",
price: 59.99,
quantity: 1,
inCart: false,
},
];
And the second one is a shopping cart
ShoppingCart.js
const ShoppingCart = (props) => {
return (
<>
<Header />
{VideoGameList.inCart === false && (
<div>
<h1 className="sc-empty-title">Your Shopping Cart Is Empty!</h1>;
<div className="shopping-cart-image">
<img src={shoppingCart} alt="empty-cart-image"></img>
</div>
</div>
)}
{VideoGameList.inCart ===
true(
<div className="videogame-content">
{VideoGameList.map(
(
{ id, title, src, releaseDate, price, quantity, inCart },
index
) => (
<div className="videogame">
<img key={src} src={src} alt={title} />
<div className="title-date">
<p className="title">{title}</p>
<p className="date">{releaseDate}</p>
</div>
<div>
<p className="price">${price}</p>
<button className="console">Add to Cart</button>
</div>
</div>
)
)}
</div>
)}
</>
);
};
export default ShoppingCart;
Under the VideoGameList object you'll notice a value of inCart set to either true or false. Using conditional render I'm trying to make it so if all inCart object values are set to false, the shopping cart will be (technically) empty and an image will be displayed. But if even a single inCart object value is set to true, that image will be displayed in its own div and the empty shopping cart image will not be displayed. However, my code appears to be wrong as when I go to the ShoppingCart page the only thing that is displayed is the Header and everything underneath is a white screen. Everything that I've tried doesn't appear to be working. Please advice.

It will be clearer and a little more efficient to reverse your logic, and check if some() of your objects are in the cart first. This has the advantage of stopping early if there is an element in the cart, instead of having to always check every element in the array. It is also much clearer to test a positive condition rather than a negated one — 'if any is true...' vs 'if every is not true ...'
To only map those elements that are in the cart you can add a filter before your map() call (if you are doing other things with the objects in the cart it may be worth handling this filtering earlier in the component or storing in state at some level).
<>
<Header />
{VideoGameList.some(v => v.inCart)
? (
<div className='videogame-content'>
{VideoGameList.filter(v => v.inCart).map( // filter before mapping ({ id, title, src, releaseDate, price, quantity, inCart }, index) => (
<div className='videogame'>
<img key={src} src={src} alt={title} />
<div className='title-date'>
<p className='title'>{title}</p>
<p className='date'>{releaseDate}</p>
</div>
<div>
<p className='price'>${price}</p>
<button className='console'>Add to Cart</button>
</div>
</div>
),
)}
</div>
)
: (
<div>
<h1 className='sc-empty-title'>Your Shopping Cart Is Empty!</h1>;
<div className='shopping-cart-image'>
<img src={shoppingCart} alt='empty-cart-image'></img>
</div>
</div>
)}
</>;

VideoGameList.inCart is undefined that's why doesn't catch none of your conditions. VideoGameList is an array of objects and you can't access it like that. For example this will work, but is not dynamic and doesn't help in your case VideoGameList[0].inCart. I would suggest to use
VideoGameList.every((v) => v.inCart // this will return for every object in array his inCart value

Related

How can I change the Boolean value of an object to true via onClick?

I'm creating a Shopping Cart and I have three components.
The first component is an object which carries data of recent video games.
VideoGameList.js
const VideoGameList = [
{
id: 1,
title: "Fire Emblem Engage",
price: 59.99,
inCart: false,
},
{
id: 2,
title: "Pokemon Violet",
price: 59.99,
inCart: false,
},
];
The second component is the main Video Game page which displays each video game in a nice div. Inside of each div is an Add to Cart button which, if clicked, is meant to set the value of inCart (of that object) from false to true but I'm having trouble. This is the code I'm using.
VideoGame.js
const VideoGame = () => {
const [cart, setCart] = useState(VideoGameList);
const handleCartStatus = (cart) => {
setCart((prevState) =>
prevState.map((value) => {
if (value.id === cart.id) {
return {
...value,
inCart: !value.inCart,
};
}
return value;
})
);
};
return (
<>
<Header />
<div>
{VideoGameList.map(
({ id, title, price, inCart }, index) => (
<div>
<img key={src} src={src} alt={title} />
<div>
<p>{title}</p>
<p>{releaseDate}</p>
</div>
<div>
<p>${price}</p>
<button
onClick={() => handleCartStatus(cart)}
>
Add to Cart
</button>
</div>
</div>
)
)}
</div>
</>
);
};
My handleCartStatus function is meant to map through the entire VideoGameList array and check for the value of inCart and change the status from false to true if the id of the object I'm clicking matches the one in the array. I added the function to the onClick and have been testing if the inCart boolean values change from false to true but they are not.
The reason I'm wanting to change the value from false to true brings me to my third and final component which is the ShoppingCart page which displays all VideoGameList objects with inCart values set to true in another nice div.
I feel something may be wrong with the useStatue(VideoGameList) because I'm importing the objects from another component but even adding the entire array to the useState doesn't fix anything. Please advice.

With React, what is the best way to pass a useState array back and forth between components?

I had some help with a previous issue with my little project, but I have a new problem I can't seem to understand. My program takes an array of objects (call them cards), and displays an on-screen card for each element in the array. I have an edit button for each card, which should open the edit form for the chosen item, and pre-populate it with its current state - this all works.
I want to be able to edit the item, save it back in place into the array, and have that 'card' updated. This is the main component:
import React from "react";
import ReactFitText from "react-fittext";
import Editform from "./Editform";
function Displaycards({ lastid }) {
// dummy data hardcoded for now
const [cards, setCards] = React.useState([
{
id: 1,
gamename: "El Dorado",
maxplayers: 4,
freespaces: 1,
tablenum: 5,
},
{
id: 2,
gamename: "Ticket to Ride",
maxplayers: 4,
freespaces: 2,
tablenum: 3,
},
]);
const [showForm, setShowForm] = React.useState(false);
const [currentid, setCurrentid] = React.useState(0);
return (
<div className="cardwrapper">
{cards.map(({ id, gamename, maxplayers, freespaces, tablenum }) => {
return (
<div key={id}>
<div>
<div className="card">
<ReactFitText compressor={0.8}>
<div className="gamename">{gamename}</div>
</ReactFitText>
<div className="details">
<p>Setup for: </p>
<p className="bignumbers">{maxplayers}</p>
</div>
<div className="details">
<p>Spaces free:</p>
<p className="bignumbers">{freespaces}</p>
</div>
<div className="details">
<p>Table #</p>
<p className="bignumbers">{tablenum}</p>
</div>
<button type="button" className="playbutton">
I want to play
</button>
<br />
</div>
<div className="editbuttons">
<button
type="button"
className="editbutton"
onClick={() => {
setShowForm(!showForm);
setCurrentid(id);
}}
>
Edit
</button>
<button type="button" className="delbutton">
X
</button>
</div>
</div>
</div>
);
})}
{showForm && (
<div>
<Editform cards={cards} setCards={setCards} id={currentid} />
</div>
)}
</div>
);
}
export default Displaycards;
and this is the Editform.js which it calls at the bottom. As far as I can tell I'm passing my array, setter function, and id of the card I want to edit:
function Editform({ cards, setCards, id }) {
const thisCard = cards.filter((card) => card.id === id)[0];
const editThisCard = thisCard.id === id; // trying to match id of passed card to correct card in 'cards' array.
console.log(editThisCard);
function saveChanges(cardtochange) {
setCards(
cards.map(
(
card // intention is map back over the original array, and if the id matches that
) =>
card.id === id // of the edited card, write the changed card back in at its ID
? {
id: id,
gamename: cardtochange.gamename,
maxplayers: cardtochange.maxplayers,
freespaces: cardtochange.freespaces,
tablenum: cardtochange.tablenum,
}
: card // ... or just write the original back in place.
)
);
}
return (
<>
{editThisCard && ( // should only render if editThisCard is true.
<div className="form">
<p>Name of game:</p>
<input type="text" value={thisCard.gamename}></input>
<p>Max players: </p>
<input type="text" value={thisCard.maxplayers}></input>
<p>Free spaces: </p>
<input type="text" value={thisCard.freespaces}></input>
<p>Table #: </p>
<input type="text" value={thisCard.tablenum}></input>
<p></p>
<button
type="button"
className="playbutton"
onClick={saveChanges(thisCard)} //remove to see edit form - leave in for perpetual loop.
>
Save changes
</button>
</div>
)}
</>
);
}
export default Editform;
If I comment out the onClick for the button, the page renders. If it's in there, the page gets stuck in an infinite loop that even React doesn't catch.
The way I'm trying to recreate my array is based on advice I've read here, when searching, which said to take the original array, rebuild it item-for-item unless the ID matched the one I want to change, and to then write the new version in.
I suspect there might be a way to do it with my setter function (setCards), and I know there's an onChange available in React, but I don't know a) how to get it to work, or b) how - or even if I need to - pass the changed array back to the calling component.
Your function is invoked directly upon components render:
{saveChanges(thisCard)}
Rename it to a callback style signature:
{() => saveChanges(thisCard)}
Also do add a jsfiddle/ runnable snippet for answerers to test ✌️.
Edit:
About the array of objects passing and updates, at your part the code is good where filter is used. We can apply idea of moving update logic to parent where data is located.
Now id + updated attributes could be passed to the update callback in child.
To give you hint, can use spread operator syntax to update items out of existing objects.

How to .Map over different props that are passed into a component?

I'm new to React but hopefully someone can help!
So I've just created a component that takes in a value (via prop) and then .maps over that value creating an Image slider. The props are all an array of objects that contain different values such as :
const Songs = [
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
{
artist: 'Artist Name',
song: 'Song Name',
lenght: '2:36',
poster: 'images/....jpg'
},
]
I have been making the same component over and over again because I don't know how to make the 'prop'.map value dynamic. Essentially I don't know how to change the value before the .map each different prop.
Here's an example. I want to make 'Songs'.map dynamic so the new props can replace that so they can also be mapped. Maybe there's another way. Hopefully some can help.
import React from 'react';
import { FaCaretDown } from 'react-icons/fa';
function ImageSlider({Songs, KidsMovies, Movies, TvShows}) {
return (
<>
{Songs.map((image, index) => (
<div className="movie-card">
<img src={'https://image.tmdb.org/t/p/w500' + image.poster_path}
className='movie-img' />
<h5 className='movie-card-desc'>{image.original_title}</h5>
<p className='movie-card-overview'>{movie.overview}</p>
</div>
))}
</>
);
}
export default ImageSlider;
Given your example,
I feel like all you need is render ImageSlides for each array
function ImageSlider({ items }) {
return (
<>
{items.map((item, idx) => (
<div ... key={idx}> // be careful to not forget to put a key when you map components
...
</div>
))}
</>
);
}
When rendering your component
function OtherComponent({ songs, kidsMovies, movies, tvShows }) {
return (
<div>
<ImageSlider items={songs} />
<ImageSlider items={kidsMovies} />
<ImageSlider items={movies} />
<ImageSlider items={tvShows} />
</div>
);
}

Objects are not valid as a React child (found: object with keys {Dname, recipe_diet}). If you meant to render a collection of children, use an array

I can get all my api info, but when create a new "recipe" and I try to show it on the Front, I get that error, but just on the "recipe" that I created, :c
import React from "react";
import './estilos/Cartita.css'
export default function Card({image, title, diets, healthScore, createdInBd, id}){
return (
<div className="containerr">
<div className="card">
<h2>{id}</h2>
<img src={image} alt="img not found" width="200px" height="250px"/>
<h2>NOMBRE:</h2>
<h5>{title}</h5>
<p>Tipo de dieta</p>
<div >
{
(!diets.length)?
<span>This Recipe doesnt have a diet
</span> :
diets.map((diet) => (
<span><li key={diets}> {diet}</li></span>
)
)}
</div>
<h2>HEALTSCORE:</h2>
<h5>{healthScore}</h5>
<h5>{createdInBd}</h5>
</div>
</div>
);
}
and this is the Api info, the top one is from the api and the other one is the one that I created:
enter image description here
due to your screenshot diets can be array of strings or array of objects, therefore you need to check if diet is an object
{!diets.length ?
<span>This Recipe doesnt have a diet</span> :
diets.map((diet) => (
<span><li key={diet.id}>{typeof diet === 'string' ? diet : diet.Dname}</li></span>
)
)}

Use props from component in another file

As you might understand, I'm pretty new to React. I tried to search StackOverflow and google this, but I cannot find anything that answers this question.
I'm creating this travel review page. I save a form to a Firebase database and I display it using a component. However, I want to display, say, only the name of a country, but I cannot manage to do that. I'm pretty sure that I must pass it as a prop but I cannot figure out how to do that, exactly.
What I want is to, use this component and decide in the component what to view.
If I'd use this component in some other .js-file, I'd like to do something like this:
and display only the data that lies withing the field countryName.I'm going to use the country names as a list, that's why I want to single out just the names.
I've played around using props but I cannot get my head around it. Perhaps someone could help out?
Thanks.
This is the function which I use as a component:
const CountryList = () => {
const countries = useCountries()
return (
<div className="countries">
{countries.map((country) =>
<div key={country.id}>
<div className="time-entry">
Name of review: {country.revName} <br/>
Name of Country: {country.countryName}<br/>
Destination 1: {country.dest1}<br/>
Destination 2: {country.dest2}<br/>
Destination 3: {country.dest3}<br/>
Beer price: {country.beerPrice}<br/>
Food price: {country.foodPrice}<br/>
Hostel price: {country.hostelPrice}<br/>
Review: {country.review}<br />
<img src={country.url} alt="no-img" />
</div>
</div>
)}
</div>
)
}
Super simple way could be to pass an array of "fields" you want to display as a prop, and conditionally render UI. If the displayFields array includes the name of the country property then render that "field".
Example:
const CountryList = ({ displayFields = [] }) => {
const countries = useCountries();
return (
<div className="countries">
{countries.map(country => (
<div key={country.id}>
<div className="time-entry">
{displayFields.includes("revName") && (
<div>Name of review: {country.revName}</div>
)}
{displayFields.includes("countryName") && (
<div>Name of Country: {country.countryName}</div>
)}
{displayFields.includes("dest1") && (
<div>Destination 1: {country.dest1}</div>
)}
{displayFields.includes("dest2") && (
<div>Destination 1: {country.dest2}</div>
)}
{displayFields.includes("dest3") && (
<div>Destination 1: {country.dest3}</div>
)}
{displayFields.includes("beerPrice") && (
<div>Beer price: {country.beerPrice}</div>
)}
{displayFields.includes("foodPrice") && (
<div>Food price: {country.foodPrice}</div>
)}
{displayFields.includes("hostelPrice") && (
<div>Hostel price: {country.hostelPrice}</div>
)}
{displayFields.includes("review") && <div>Review: {country.review}</div>}
{displayFields.includes("imgUrl") && <img src={country.url} alt="no-img" />}
</div>
</div>
))}
</div>
);
};
Usage:
<CountryList displayFields={["countryName", "beerPrice"]} />

Categories

Resources