How to get the dispatch to recognize each product when the button is clicked?
When the HTML is mapped through the examples array, there is an index given. But when I click the button to addToCart, the elements of objects of array, it shows undefined
{type: "ADD_TO_CART", item: {…}}
item: {id: undefined, name: undefined, price: undefined, desc: undefined, type: undefined, …}
type: "ADD_TO_CART"
This is Menu.js
import React, { useState } from 'react';
import examples from './examples';
import './Menu.css';
import { useStateValue } from './StateProvider';
const Menu = ({ id, name, imgUrl, desc, price, type }) => {
const [dishes, setDishes] = useState(examples);
const [, dispatch] = useStateValue();
const addToCart = () => {
// add item to basket
dispatch({
type: 'ADD_TO_CART',
item: {
id,
name,
price,
desc,
type,
imgUrl,
},
});
};
return (
<div className='menu'>
<h1>Menu</h1>
<div className='menu__starters'>
<h1>Starters</h1>
{dishes.map((dish, index) => (
<div className='menu__column'>
<div className='menu__row'>
<div className='menu__card' key={index}>
<div className='menu__img'>
<img src={dish.imgUrl} alt='img' />
</div>
<div className='menu__desc'>
<div className='menu__name'>
<h2>{dish.name}</h2>
</div>
<div className='menu__description'>
<p>{dish.desc}</p>
</div>
<div className='menu__credentials'>
<div className='menu__price'>
<h5>Damage- ${dish.price}</h5>
</div>
<div className='menu__button'>
<button onClick={addToCart} key={index}>
Add to Cart ->
</button>
</div>
</div>
</div>
</div>
</div>
</div>
))}
</div>`
An array of objects is in another file examples.js which is exported.
This is reducer.js
export const initialState = {
cart: [],
};
function reducer(state, action) {
console.log(action);
switch (action.type) {
case 'ADD_TO_CART':
// logic for adding item to cart
return { state };
break;
case 'REMOVE_FROM_CART':
//logic for removing item from cart
return { state };
break;
default:
return state;
}
}
export default reducer;`
Blockquote
It's undefined because those variables are not defined in addToCart scope, you haven't passed any data to it.
You have to pass the dish into addToCart
<button onClick={()=>addToCart(dish)} ...>
And
const addToCart = ({ id, name, imgUrl, desc, price, type }) => {...}
Related
I have a app that adds a product to the cart. I implement the application through the Redux Toolkit. When I click on the "Add to cart" button, all information about the product should be displayed in the cart.
Now the problem is that the added goods do not get into the array. An empty array is output to the console each time. How can you I this?
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
productsArr: {
items: [],
},
};
const cartSlice = createSlice({
name: "cart",
initialState,
reducers: {
addItemToCart: (state, action) => {
state.productArr.items.concat([
{
id: action.payload.productId,
title: action.payload.productTitle,
},
]);
state.productArr.totalQuantity += 1;
},
},
});
// these exports should stay the way they are
export const { addItemToCart} = cartSlice.actions;
export default cartSlice.reducer;
const ProductItem = (props) => {
const { title, price, description, id } = props;
const productTitle = title;
const productId = id;
const dispatch = useDispatch();
const addToCartHandler = () => {
console.log(addItemToCart({ productId, productTitle }));
dispatch(addItemToCart({ productId, productTitle }));
};
return (
<li className={classes.item}>
<Card>
<header>
<h3>{title}</h3>
<div className={classes.price}>${price.toFixed(2)}</div>
</header>
<p>{description}</p>
<div className={classes.actions}>
<button onClick={addToCartHandler}>Add to Cart</button>
</div>
</Card>
</li>
);
};
const Cart = (props) => {
const cartItems = useSelector((state) => state.cart.productArr.items);
console.log(cartItems);
return(
<Card className={classes.cart}>
<h2>Your Shopping Cart</h2>
<ul>
{cartItems.map((cartItem) => (
<CartItem
item={{
title: cartItem.title,
// quantity: item.quantity,
// total: item.totalPrice,
// price: item.price,
key: cartItem.id,
}}
/>
))}
</ul>
</Card>
)
};
I think it should be
State.productArr.items.push(
{
id: action.payload.productId,
title: action.payload.productTitle,
});
Did you try with push method??
I am still pretty new and am doing a simple food ordering app in React. I have a file of dummy meals and am able to create a cart through user input, create a modal on the "Your Cart" button from the header, and map through the cart displaying as a list, but I am unable to figure out how to delete an item from the cart. I realize I need to target the id of the list item that the X is connected to, but for some reason I can't get the this command to work.
Is there something about the this command that I am not understanding when it comes to a mapped list item? So far I have both tried this through my CartModal component and the Home component, but both turn up undefined when using this and I throw an error when using e.target (enter different options like e.target.value, e.target.id, etc...), and have even tried working on an inline targeting (which is where the code below is at currently) but still comes up nothing.
I will add a couple dummy meals to this as well.
Thanks so much!
const initialState = 0;
const reducer = (state, action) => {
switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return initialState;
default:
return state;
}
};
export const CountContext = React.createContext();
const Home = (props) => {
const [cart, setCart] = useState([]);
const [count, dispatch] = useReducer(reducer, initialState);
const {isVisible, toggleModal} = useModal();
let totalPrice;
let cartCounter ;
const cartUpdaterHandler = (cartItem) =>{
setCart([...cart, cartItem]);
cartItem= cartItem;
}
useEffect(() => {
cartCounter = cart.length;
totalPrice = cart.reduce((acc, {price}) => parseFloat(acc) + parseFloat(price), 0).toFixed(2);
}, [cart])
const modalHandler = () => {
toggleModal(true)
}
const removeCI = () => {
console.log(cart)
}
return (
<CountContext.Provider
value={{ countState: count,
countDispatch: dispatch,
currentCart: cart
}}
className={classes.contextProvider}
>
{isVisible && (
<CartModal
overlayClassName="custom_overlay"
isVisible={isVisible}
hideModal={toggleModal}
currentCart={cart}
totalPrice={totalPrice}
remove={removeCI}
/>
)}
<Header cart={cart} cartCounter={cart} modal={modalHandler}/>
<Intro />
<MealForm onAdd={cartUpdaterHandler} />
{/* <Cart /> */}
{/* <CartModal isVisible={isVisible} hideModal={toggleModal} currentCart={cart} totalPrice={totalPrice}/> */}
</CountContext.Provider>
)
}
export default Home;
const CartModal = ({ isVisible, hideModal, currentCart, remove}) => {
// const checkKey = () => {
// console.log(cartItem.id)
// console.log(this.id)
// }
return isVisible
? createPortal(
<Card className={classes.modal}>
<div className={classes.modalBackground}>
<div className={classes.modalDiv}>
<div className={classes.modalHeader}>
<h5 className={classes.modalHeaderH5}>Your Cart</h5>
<button className={classes.modalHeaderX} onClick={hideModal}> X </button>
</div>
<div className={classes.modalBody}>
{currentCart.map((cartItem, i) => {
return<div key={Math.random()}>
<ul className={classes.cartModalItem} key={Math.random()}>
<div className={classes.cartModalItemInfo} >
<li className={classes.cartModalName}>{cartItem.name}</li>
<li className={classes.cartModalPrice}>{cartItem.price}</li>
<li className={classes.cartModalDesc}>{cartItem.description}</li>
</div>
//I am asking about the X below this.
<button className={classes.cartModalX} onClick={()=>{console.log(this)}}>X</button>
</ul>
</div>
})}
<h5 className={classes.modalTotal}>Total:{currentCart.reduce((acc, {price}) => parseFloat(acc) + parseFloat(price), 0).toFixed(2)} </h5>
</div>
<div className={classes.modalFooter}>
<button className={classes.closeModalButton} onClick={hideModal}> Close </button>
<button className={classes.orderModalButton}>Checkout</button>
</div>
</div>
</div>
</Card>,
document.body,
)
: null;
};
export default CartModal;
const AvalibleMeals = [
{
id: 'm1',
name: 'Sushi',
description: 'Finest fish and veggies',
price: 22.99,
},
{
id: 'm2',
name: 'Schnitzel',
description: 'A german specialty!',
price: 16.5,
},
{
id: 'm3',
name: 'Barbecue Burger',
description: 'American, raw, meaty',
price: 12.99,
},
{
id: 'm4',
name: 'Green Bowl',
description: 'Healthy...and green...',
price: 18.99,
},
];
export default AvalibleMeals;
I`m trying to use a reducer function to update state. I have a state variable, phones, which is an array of phone objects. When the user clicks a button, I want the quantity property of the phone object to increase. Quantity does get increased, but there's no change in the UI.
Here`s my code:
const initialState = {
phones: [...Phones]
};
const reducer = (state, action) => {
if (action.type === "increase") {
const newPhones = state.phones.map(phone => {
if (phone.id === action.payload) {
return { ...phone, quantity: phone.quantity + 1 };
}
return phone;
});
return { ...state, phones: newPhones };
}
};
const Cart = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div className="cart_div">
<h1>Your Bag</h1>
{Phones.map(phone =>
<PhoneListItem
key={phone.id}
dispatch={dispatch}
state={state}
{...phone}
/>
)}
</div>
);
};
const PhoneListItem = ({ name, price, img, quantity, dispatch, id, state}) => {
return (
<div className="phone_row">
<img className="phone_img" src={img} />
<div className="phone_info_div">
<h4>
{name}
</h4>
<h4 className="price">
{price}
</h4>
</div>
<div className="phone_amount_div">
//user clicks on button to increase phone quantity
<button className="increase_button" onClick={() => dispatch({ type: "increase", payload: id })}>
<Icon icon="akar-icons:chevron-up" />
</button>
<p className="phone_amount_para">{quantity}</p>
<button className="decrease_button" onClick={() => dispatch({ type: "decrease", payload: id })}>
<Icon icon="akar-icons:chevron-down" />
</button>
</div>
</div>
);
};
useReducer will return the whole state instead of a state slice.
You already get the state by array destructuring, but you didn't use it.
const [state, dispatch] = useReducer(reducer, initialState);
{state.Phones.map(phone => {
return <PhoneListItem
key={phone.id}
dispatch={dispatch}
state={state}
{...phone}
/>
})}
How can I add a search function the data below (Ideally in another component, Nav Component)? I have tried passing the data via props and context and simply can not get it to work. I need to be able to filter through the data by Album name, Artist or potentially another attribute.
import React, { Component, useEffect, useState} from 'react'
import axios from 'axios';
import '../components/data.css';
function Data() {
const [Music, setMusic] = useState([], {});
useEffect(() => {
axios.get('https://itunes.apple.com/us/rss/topalbums/limit=100/json')
.then((data) => {
setMusic(data.data.feed.entry);
})
.catch((err) => console.log(err));
},[]);
return (
<div>
<article className="albums">
<ul className="album-items">
{Music.map(Music => {
const { id, name, label, title, artist, entry } = Music;
return (
<div className="album" >
<li className='album-list' key={id.attributes['im:id']}>
<h3 className='title'>Album Name: {Music['im:name'].label}</h3>
<img src={Music['im:image'][2].label} alt={Music['im:name'].label}/>
<h4 className='title'>Artist: {Music['im:artist'].label}</h4>
</li>
</div>
);
})}
</ul>
</article>
</div>
)
}
export default Data
Here is how you can search the album data based on album name and artist name.
Working App: Stackblitz
import React, { Component, useEffect, useState } from "react";
import axios from "axios";
function Data() {
const [Music, setMusic] = useState([], {});
const [search, setSearch] = useState("");
useEffect(() => {
axios
.get("https://itunes.apple.com/us/rss/topalbums/limit=100/json")
.then(data => {
setMusic(data.data.feed.entry);
})
.catch(err => console.log(err));
}, []);
//======================================
const handleSearch = event => {
console.log(event.target.value);
setSearch(event.target.value.toLowerCase());
};
const filtered = Music.length
? Music.filter(
music =>
music["im:name"]["label"].toLowerCase().includes(search) ||
music["im:artist"]["label"].toLowerCase().includes(search)
)
: [];
//======================================
return (
<div>
<input onChange={handleSearch} placeHolder={"search title, artist"} />
<article className="albums">
<ul className="album-items">
{filtered.map(Music => {
const { id, name, label, title, artist, entry } = Music;
return (
<div className="album">
<li className="album-list" key={id.attributes["im:id"]}>
<h3 className="title">
Album Name: {Music["im:name"].label}
</h3>
<img
src={Music["im:image"][2].label}
alt={Music["im:name"].label}
/>
<h4 className="title">Artist: {Music["im:artist"].label}</h4>
</li>
</div>
);
})}
</ul>
</article>
</div>
);
}
export default Data;
Hello I have an action to get data from my backend and then I created a new container and I use this state and I created an action to filter this state, and then when it clicks back to my component product the products that were filtered
My action fetch:
export const fetchProduct = () => {
console.log('action')
return dispatch => {
dispatch(fetchStarted());
api
.get('/products')
.then(res => {
dispatch(fetchSucess(res.data));
})
.catch(err => {
dispatch(fetchFailed(err.message));
});
};
};
My reducer fetch:
const initialState = {
loading: false,
products: [],
error: null
};
export default function productReducer(state = initialState, action) {
switch (action.type) {
case FETCH_LOADING:
return {
...state,
loading: true
};
case FETCH_SUCESS:
return {
...state,
loading: false,
error: null,
...state, products: action.data
};
case FETCH_FAIL:
return {
...state,
loading: false,
error: action.error
};
default:
return state;
}
}
My container product:
class HomeProducts extends Component {
componentDidMount() {
this.props.fetchProduct();
}
render() {
const productItems = this.props.products.map( product =>(
<div className="col-md-4 pt-4 pl-2" key={product.id}>
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
)
return (
<div className="container">
<div className="row">
{productItems}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return{
products: state.data.products,
loading: state.data.loading,
error: state.data.error
}
};
const mapActionsToProps = {
fetchProduct: fetchProduct
};
export default connect(mapStateToProps, mapActionsToProps)(HomeProducts);
My product container will show in a div all the products it fetched in the backend.
And now My container filter:
class FilterHome extends Component {
render() {
console.log(this.props);
return (
<>
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<p>Stat Trak</p>
<p>Normal</p>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<p value={2} onChange={(e) => this.props.filterProducts(this.props.products, e.target.value)}>Factory New ()</p>
<p>Minimal Wear ()</p>
<p>Field Tested ()</p>
<p>Well Worn ()</p>
<p>Battle Scarred ()</p>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = state => ({
products: state.data.products,
});
export default connect(mapStateToProps, {filterProducts})(FilterHome);
and then I created an action and a redux to get the filtered product list and then send this filtered stripe to my div that displays the products.
Action filter products:
import {FILTER_PRODUCT} from '../constants/fetchTypes';
export const filterProducts = (products,value) => (dispatch) => {
return dispatch({
type:FILTER_PRODUCT,
payload:{
value:value,
products:value === ''? products: products.filter(v => v.id_sub = value)
}
})
}
My reducer filter:
import {
FETCH_SUCESS,
FETCH_FAIL,
FETCH_LOADING,
FILTER_PRODUCT
} from '../constants/fetchTypes';
const initialState = {
products: []
};
export default function productReducer(state = initialState, action) {
switch (action.type) {
case FILTER_PRODUCT:
return {
...state,
filteredProducts: action.payload.products
};
default:
return state;
}
} return state;
}
}
I don't know how I can change the filtered products in my product div