Cart not adding correct item in React.js - javascript

I'm learning React.js (and I'm a beginner at JavaScript) and having some difficulty getting my addToCart function working properly. I've managed to get all my products to display but adding to cart on any of them only adds the first item, and once that item is out of stock then all the items go out of stock.
I'm sure I'm missing something obvious but would really appreciate some more experienced eyes on my code please.
Github repo for this app
Below are my FluxProduct.react.js product components:
var React = require('react');
var FluxCartActions = require('../actions/FluxCartActions');
var FluxProduct = React.createClass({
addToCart: function(event){
var id = this.props.selected.id;
var update = {
name: this.props.product.name,
category: this.props.selected.category,
price: this.props.selected.price
}
FluxCartActions.addToCart(id, update);
FluxCartActions.updateCartVisible(true);
},
render: function() {
var self = this;
var products = this.props.product;
var stockAvailable = (this.props.selected.id in this.props.cartitems) ? this.props.selected.stock - this.props.cartitems[this.props.selected.id].quantity : this.props.selected.stock;
return (
<ul>
{Object.keys(products).map(function(product){
return (
<li key={product}>
<div className="flux-product">
<img src={'img/' + products[product].image}/>
<div className="flux-product-detail">
<h1 className="name">{products[product].name}</h1>
<p className="category">{products[product].category}</p>
<p className="description">{products[product].description}</p>
<p className="price">Price: ${products[product].price}</p>
<button type="button" onClick={self.addToCart} disabled={stockAvailable > 0 ? '' : 'disabled'}>
{stockAvailable > 0 ? 'Add To Cart' : 'Sold Out'}
</button>
</div>
</div>
</li>
)
})}
</ul>
);
},
});
module.exports = FluxProduct;
Relevant FluxCartApp.react.js components:
render: function(){
return (
<div className="flux-cart-app">
<FluxCart products={this.state.cartItems} count={this.state.cartCount} total={this.state.cartTotal} visible={this.state.cartVisible} />
<FluxProduct product={this.state.product} cartitems={this.state.cartItems} selected={this.state.selectedProduct} />
</div>
);
},
Relevant cart actions:
selectProduct: function(index){
AppDispatcher.handleAction({
actionType: FluxCartConstants.SELECT_PRODUCT,
data: index
})
},
addToCart: function(id, update){
AppDispatcher.handleAction({
actionType: FluxCartConstants.CART_ADD,
id: id,
update: update
})
},
Sorry but I'm not sure what further relevant code needs to be pasted, if anyone has any advice I'd be grateful.

You're accessing the products variable from inside your map function, when instead you should be using the product parameter that is passed to it. Additionally, why map on the keys of products, just us the products as an array:
<ul>
{products.map(function(product){
return (
<li key={product}>
<div className="flux-product">
<img src={'img/' + product.image}/>
<div className="flux-product-detail">
<h1 className="name">{product.name}</h1>
<p className="category">{product.category}</p>
<p className="description">{product.description}</p>
<p className="price">Price: ${product.price}</p>
<button type="button" onClick={self.addToCart.bind(null, product.id)} disabled={stockAvailable > 0 ? '' : 'disabled'}>
{stockAvailable > 0 ? 'Add To Cart' : 'Sold Out'}
</button>
</div>
</div>
</li>
)
})}
</ul>
Notice that I've used bind to pass the product ID through to addToCart as well, and this lets us use it like this, with the bound ID passed as the first argument:
addToCart: function(id, event){
var update = {
name: this.props.product.name,
category: this.props.selected.category,
price: this.props.selected.price
}
FluxCartActions.addToCart(id, update);
FluxCartActions.updateCartVisible(true);
}
You may also be interested in the answer "JavaScript variable binding and loop" which may have been a contributing factor in your problem and often catches out JavaScript newcomers.

Related

react Creating dynamic components

I have li key={Info.id}>
I want to make <div>{NumberStatus} </div> output the data corresponding to the id value when the corresponding id value is clicked.
In summary, when 'one li tag text' is clicked, the div will output '{NumberStatus}' value corresponding to 'one text', and {functionInfolabels} corresponding value.
How would you like to write code?
let functionInfolabels = ProductDetail && ProductDetail.chart?.functionalsInfo?.[0].materialsInfo?.[0].ingredientsInfo?.map(array => array.ingredientDisplayText);
let NumberStatus = ProductDetail && ProductDetail.chart?.functionalsInfo?.[0].materialsInfo?.[0].ingredientsInfo?.map(array => array.chartStatus)
return (
{ProductDetail && ProductDetail.chart?.functionalsInfo?.length ?
ProductDetail.chart?.functionalsInfo.map(Info => (
<li key={Info.id}>{Info.seedShapeDisplayText}</li>
)) : <li>There is not</li>}
// seedShapeDisplayText; // one two three four five ...
// <li key===1 onClick <div> Hi Number Minsu, Bar : 1 </div>
// <li key===2 onClick <div> Hi Number Jenny, Bar : 3 </div>
....
<div>
Hi Number {NumberStatus} // one : Minsu, two : Jenny, three : Youngmin, four : Jiho ...
<Bar
labels={functionInfolabels} // one : 1, two: 3, three: 124 ....
/>
</div>
)
The most idiomatic way is to set state value when your <li> element is clicked, and then render that state value.
Using a functional component, you can use the useState hook in a straight forward manner.
Please note: I took some liberty with your code sample, and added a button tag, and a helper function to set the value.
const MyComponent = () => {
const [infoText, setInfoText] = React.useState('');
const onButtonClick = (text) => {
setInfoText(text);
}
return (
{ProductDetail && ProductDetail.chart?.functionalsInfo?.length ?
ProductDetail.chart?.functionalsInfo.map(Info => (
<li key={Info.id}><button onClick={e=>onButtonClick(Info.seedShapeDisplayText)}>{Info.seedShapeDisplayText}</button></li>
)) : <li>There is not</li>}
// seedShapeDisplayText; // one two three four five ...
<div>
Hi Number {infoText} // one : Minsu, two : Jenny, three : Youngmin, four : Jiho ...
</div>
)
}
If you could explain a bit more on your question, I think it will be easy to understand and answer.
Here is the solution as I got your question,
in here I'm using the state as numberStatus and when your click on any of the li i'm setting the numberStatus state with the numberStatus data of the selected li.
also in the bottom rendering part I'm checking that the state of numberStatus is having a value and if only I make it display.
function MyComponent() {
const [numberStatus, setNumberStatus] = React.useState("");
const dataList = [
{ title: "Title 01", numberStatus: "one" },
{ title: "Title 02", numberStatus: "two" },
{ title: "Title 03", numberStatus: "three" },
];
return (
<div>
<ul>
{dataList.map((val, key) => {
return (
<li key={key} onClick={() => setNumberStatus(val.numberStatus)}>
{" "}
{val.title}{" "}
</li>
);
})}
</ul>
{numberStatus && <div>Hi Number {numberStatus}</div>}
</div>
);
}

State into index of array gives error REACT

I'm having issue where when i put my state into index of an array, it gives error.
Here is there line of bug : {WorkData[state].title}
What i need to do is to display the elements of WorkData which is a array with objects : title, content, img math etc...
const WorkData = [
{
id: 0,
title: 'title',
subtext: 'React/Express',
content: 'content',
imgPath: 'imgPath',
},
{
id: 1,
title: 'Little Hero Academy',
subtext: 'React/API REST',
content:
'content,
imgPath: 'imgPath',
},
{
id: 2,
title: 'title',
subtext: 'subtext',
content: 'content',
imgPath: 'imgPath',
},
First, i have a list of cards with a button that contains data:
<div className="work-container flex">
<div className="work-bloc zoom">
<div className="work-hover" />
<div className="work-text">
<div className="text-title">{WorkData[0].title}</div>
<span className="subtext">{WorkData[0].subtext}</span>
</div>
<div
onClick={togglePopup}
data-id={WorkData[0].id}
className="work-showmore button2"
>
Show More
</div>
</div>
</div>
I store the data in state with this function :
const [id, setId] = useState();
const togglePopup = (e) => {
setIsOpen(!isOpen);
setId(e.target.dataset.id);
};
I need that data to get to the popup,
<Popup
togglePopup={togglePopup}
closePopup={closePopup}
isOpen={isOpen}
id={id}
/>
I pass the state to my popup component and try to display the value of id which is displayed
Also,i need to make the popup display of the content of the array that belong to the id (index) i passed in state : workData 1 2 3 etc...
const Popup = ({ closePopup, isOpen, id }) => {
return (
<div className={`popup-container ${isOpen ? 'opened' : 'closed'}`}>
<div className={`popup ${isOpen ? 'opened2' : 'closed2'}`}>
<span className='popup-title'>
{WorkData[id].title}
{id}
</span>
<div className='image-container'>
<img
src='https://i.picsum.photos/id/1060/536/354.jpg?blur=2&hmac=0zJLs1ar00sBbW5Ahd_4zA6pgZqCVavwuHToO6VtcYY'
alt=''
/>
</div>
<p>
Team projet with React and API REST. Our goal was to make an app with
a choosen API and make something out of it. We did a superhero-themed
website with games for children.
<br />
Check on <AiFillGithub className='menuicon' />
</p>
<div onClick={closePopup} className='close-icon button2'>
Show less{' '}
</div>
</div>
</div>
);
};
but i get this error :
**TypeError: _WorkData__WEBPACK_IMPORTED_MODULE_1__.default[id] is undefined**
Thanks in advance for any suggestion
Ok i fixed, it i changed the way of doing it, i mapped my workcard components so i can easily retrieve the id, title, etc, and i didnt change the event onclick to catch the card.id in state, i passed the state to parent, then to the popup, and then into the popup component i just imported the WorkData.js and simply did : WorkData[state-with-id].title etc
See sample of code below
works.js
const togglePopup = (e) => {
setIsOpen(!isOpen);
setPopupId(e.target.dataset.id);
};
const closePopup = () => {
setIsOpen(false);
};
useEffect(() => {
setCards(workData);
}, []);
workcards.js
<div
onClick={togglePopup}
className='work-showmore button2'
data-id={workcard.id}
>
popup.js
import workData from './workData';
<span className='popup-title'>{workData[popupId].title}</span>

how to access children props in React

Really sorry, I know this has been posted before but I just didn't understand how the answers related to my problem. I'm very new to react and need a bit more help than I could find.
My app project has reviews like this;
const restaurantData = [
{
id: 1,
restaurantName: "Restaurant 1",
address: "4, Dolphin Way, Swansea SA10 5BZ",
lat: 48.8737815,
long: 2.3501649,
ratings: [{ ratingID: 1, stars: 2, comment: "Terrible service, Yikes!" }],
},
];
And I have accessed the data no problem like this;
function RestaurantItem({ restaurant }) {
return (
<div className="restaurantItem" id={restaurant.id}>
<h2 className="AsideHeader">{restaurant.restaurantName}</h2>
<p className="AsideAddress">{restaurant.address} </p>
</div>
);
}
I would basically like to access the review data specifically by doing something like this;
<div>
<p>{restaurant.ratings.stars} STAR : </p> {restaurant.ratings.comment}
</div>
But I am not having any luck. Can someone explain what I am doing wrong and how to address it ? Or even what else I would call this to look up the solution?
Thank You !
ratings is an array, so you can't access the data as ratings.stars or ratings.comment.
An approach would be to use map to iterate through the ratings and display all of them for that specific restaurant.
<div>
{restaurant.ratings.map((rating) => (
<p key={rating.ratingID}>{`${rating.stars} STAR: ${rating.comment}`}</p>
))}
</div>
Inside your const restaurantData the ratings is an array. If there is only 1 rating then remove the array:
ratings: { ratingID: 1, stars: 2, comment: "Terrible service, Yikes!" }
If there will be multiple ratings then use .map to loop every single one.
{restaurant.ratings.map((rating) => (
<div key={rating.ratingID}>
<p>{rating.stars} STAR : </p> {rating.comment}
</div>
))}
Since restaurant.ratings is a list you can't display it just like a string.
You could display them like this:
<div>
{restaurant.ratings.map((rating, index) => (
<p key={"rating-" + rating.ratingID}>
{rating.stars + " STAR : " + reating.comment}
</p>
))}
</div>
The map method of the list iterates over every element and returns each as "described" by the anonymous method as a JSX Element.
You call props on array, but you need call prop on element of array.
If the ratings property always has one element, then you can write it like this, but it will be a crutch
function RestaurantItem({ restaurant }) {
return (
<div className="restaurantItem" id={restaurant.id}>
<h2 className="AsideHeader">{restaurant.restaurantName}</h2>
<p className="AsideAddress">{restaurant.address} </p>
</div>
<div>
<p>{restaurant.ratings[0].stars} STAR : </p> {restaurant.ratings[0].comment}
</div>
);
}
#lich reported correctly, to work with an array, you must use the map method
using reactjs list look here
js arrays

React - one event handler to toggle one of multiple similar elements

I'm trying to practice React by rebuilding an agency website. I'm working on a section which has staff images, and clicking one of those images opens the relevant staff bio in a modal. The images and the bios are in separate containing divs.
It feels like I should be able to write one event handler that finds and opens the relevant bio depending on which image is clicked (maybe using something like the data attribute?), but I can't figure out what I'd need to add.
Currently I just have a click handler which toggles a piece of 'active' state. That state is then added as a className to toggle whether the modal is showing. Problem of course being that it doesn't differentiate between bios, so they all show regardless which bio is clicked on.
In case it's useful, here is my 'staff bio' component:
const StaffBio = (props) => {
return (
<div className={`teamMemberOverlay ${props.active}`} onClick={props.onClick}>
<div className="teamMemberExpanded">
<h6>{props.name}</h6>
<div className="seperator"></div>
<p className="title">{props.title}</p>
</div>
</div>
);
}
Which is being used like this:
<StaffBio name="NAME HERE" title="TITLE HERE" active={this.state.active} onClick={this.showBio} />
So far I've got the images set up as follows:
<img src={PaulIllustration} className="staffPhoto" onClick={this.showBio} />
And lastly, my event handler:
showBio() {
let toggle = this.state.active === 'is-active' ? '' : 'is-active';
this.setState({active: toggle});
}
class AboutUsSlider extends Component {
constructor(props) {
super(props);
this.showBio = this.showBio.bind(this)
this.next = this.next.bind(this)
this.state = { active: null }
}
next() {
this.refs.slider.slickNext()
}
showBio(id) {
this.setState({active: id});
}
hideBio(){
this.setState({active: null});
}
render() {
var settings = {...}
const people = [{name: 'Paul', title: 'some title'}, {name: 'Ben', title: 'other'}, ...];
return (
<div>
<Slider ref="slider" {...settings}>
<div className="sliderPage">
<h2>Meet our team</h2>
<div className="seperator"></div>
<div className="teamPhotos">
{ // When setting the index, you should use something unique, I'll use the name here.
people.map((p, index) =>
<img key={p.name} src={`${p.name} + 'Illustration'`} className="staffPhoto" onClick={() => this.showBio(index)}) />
}
</div>
<Button BGColor="#009ECC" text="Our process" onClick={this.next} />
</div>
</Slider>
{ this.state.active && <StaffBio name={people[this.state.active]} title={people[this.state.active].title} onClick={this.hideBio}/>
</div>
)
}
EDITED
There are a couple of things you can do.
Each person probably has an id to identify it. So you could modify your showBio to look like this:
showBio(id) {
this.setState({ active: id })
}
This way, you get which person is currently active in your state.
You also need to change your img
<img src={PaulIllustration} className="staffPhoto" onClick={() => this.showBio(PaulId)} />
Where PaulId would be different for each person.
And your StaffBio:
<StaffBio name="NAME HERE" title="TITLE HERE" active={this.state.active == personId} onClick={this.showBio} />
const StaffBio = (props) => {
return (
<div className={`teamMemberOverlay ${props.active ? 'is-active' : ''}`} onClick={props.onClick}>
<div className="teamMemberExpanded">
<h6>{props.name}</h6>
<div className="seperator"></div>
<p className="title">{props.title}</p>
</div>
</div>
);
}

Passing keys to children in React.js

I am running through a react tutorial on tutsplus that is a bit old, and the code doesn't work as it was originally written. I actually am totally ok with this as it forces me to learn more independently, however I have spent a while on a bug that I just can't figure out. The bug consists of not being able to pass on an objects key, which prevents my program from updating the state of the correct object.
First off here is the repo if you want to run this code and see it in action: https://github.com/camerow/react-voteit
I have a child component that looks like this:
var FeedItem = React.createClass({
vote: function(newCount) {
console.log("Voting on: ", this.props, " which should have a key associated.");
this.props.onVote({
key: this.props.key,
title: this.props.title,
description: this.props.desc,
voteCount: newCount
});
},
voteUp: function() {
var count = parseInt(this.props.voteCount, 10);
var newCount = count + 1;
this.vote(newCount);
},
voteDown: function() {
var count = parseInt(this.props.voteCount, 10);
var newCount = count - 1;
this.vote(newCount);
},
render: function() {
var positiveNegativeClassName = this.props.voteCount >= 0 ?
'badge badge-success' :
'badge badge-danger';
return (
<li key={this.props.key} className="list-group-item">
<span className={positiveNegativeClassName}>{this.props.voteCount}</span>
<h4>{this.props.title}</h4>
<span>{this.props.desc}</span>
<span className="pull-right">
<button id="up" className="btn btn-sm btn-primary" onClick={this.voteUp}>↑</button>
<button id="down" className="btn btn-sm btn-primary" onClick={this.voteDown}>↓</button>
</span>
</li>
);
}
});
Now when someone hits the vote button the desired behavior is for the FeedItem.vote() method to send an object up to the main Feed component:
var FeedList = React.createClass({
render: function() {
var feedItems = this.props.items;
return (
<div className="container">
<ul className="list-group">
{feedItems.map(function(item) {
return <FeedItem key={item.key}
title={item.title}
desc={item.description}
voteCount={item.voteCount}
onVote={this.props.onVote} />
}.bind(this))}
</ul>
</div>
);
}
});
Which should pass that key on throught the parent component's onVote function:
var Feed = React.createClass({
getInitialState: function () {
var FEED_ITEMS = [
{
key: 1,
title: 'JavaScript is fun',
description: 'Lexical scoping FTW',
voteCount: 34
}, {
key: 2,
title: 'Realtime data!',
description: 'Firebase is cool',
voteCount: 49
}, {
key: 3,
title: 'Coffee makes you awake',
description: 'Drink responsibly',
voteCount: 15
}
];
return {
items: FEED_ITEMS,
formDisplayed: false
}
},
onToggleForm: function () {
this.setState({
formDisplayed: !this.state.formDisplayed
});
},
onNewItem: function (newItem) {
var newItems = this.state.items.concat([newItem]);
// console.log("Creating these items: ", newItems);
this.setState({
items: newItems,
formDisplayed: false,
key: this.state.items.length
});
},
onVote: function (newItem) {
// console.log(item);
var items = _.uniq(this.state.items);
var index = _.findIndex(items, function (feedItems) {
// Not getting the correct index.
console.log("Does ", feedItems.key, " === ", newItem.key, "?");
return feedItems.key === newItem.key;
});
var oldObj = items[index];
var newItems = _.pull(items, oldObj);
var newItems = this.state.items.concat([newItem]);
// newItems.push(item);
this.setState({
items: newItems
});
},
render: function () {
return (
<div>
<div className="container">
<ShowAddButton displayed={this.state.formDisplayed} onToggleForm={this.onToggleForm}/>
</div>
<FeedForm displayed={this.state.formDisplayed} onNewItem={this.onNewItem}/>
<br />
<br />
<FeedList items={this.state.items} onVote={this.onVote}/>
</div>
);
}
});
My logic relies on being able to reconcile the keys in the onVote function, however the key prop is not being properly passed on. So my question is, how do I pass on they key through this 'one way flow' to my parent component?
Note: Feel free to point out other problems or better design decision, or absolute stupidities. Or even that I'm asking the wrong question.
Looking forward to a nice exploration of this cool framework.
The key prop has a special meaning in React. It is not passed to the component as a prop, but is used by React to aid the reconciliation of collections. If you know d3, it works similar to the key function for selection.data(). It allows React to associate the elements of the previous tree with the elements of the next tree.
It's good that you have a key (and you need one if you pass an array of elements), but if you want to pass that value along to the component, you should another prop:
<FeedItem key={item.key} id={item.key} ... />
(and access this.props.id inside the component).

Categories