I am building a basket ecommerce app and my add to cart function is working fine, however when I use my remove item function it does not work, in fact it just adds the same item to the cart again but then the total displays as NaN.
I am just using .filter to return a new array without the items that I want removed by using its id, so not sure how this is happening.
If you want to replicate what I am talking about in code sandbox just click the add to cart button - then click top right basket icon to go to basket page - then click the minus button to remove the item.
code sandbox here
code below:
CartReducer.js
import {ADD_TO_CART, REMOVE_ITEM} from '../Types'
export const CartReducer = (state, action) => {
switch (action.type) {
case ADD_TO_CART: {
return {
...state,
cartItems: [...state.cartItems, action.payload],
}
}
case REMOVE_ITEM: {
return {
...state,
cartItems: state.cartItems.filter((item) => item.id !== action.payload.id),
}
}
default:
return state
}
}
CartState.js
import { useReducer } from 'react'
import { CartContext } from './CartContext'
import {CartReducer} from './CartReducer'
import { SHOW_HIDE_CART, ADD_TO_CART, REMOVE_ITEM } from '../Types'
import {products} from '../../pages/ProductDetailsPage'
export const CartState = ({children}) => {
const initialState ={
// showCart: false,
products: products,
cartItems: [],
};
const [state, dispatch] = useReducer(CartReducer, initialState);
const addToCart = (item) => {
dispatch({type: ADD_TO_CART, payload: item})
};
const removeItem = (id) => {
dispatch({ type: REMOVE_ITEM, payload: id });
};
return (
<CartContext.Provider
value={{
products: state.products,
cartItems: state.cartItems,
addToCart,
removeItem,
}}>
{children}
</CartContext.Provider>
)
};
BasketItem.js
import React, { useContext } from 'react'
import image from '../assets/image.png'
// import { QuantityButtonDiv } from '../components/QuantityButtonDiv'
import plusButtonImage from '../assets/vector+.png'
import subtractButtonImage from '../assets/vector.png'
import { CartContext } from '../context/cart/CartContext'
export const BasketItem = ({item}) => {
const { cartItems, removeItem } = useContext(CartContext);
return (
<div className="basket-item">
<div className="title-div">
<span>
{item.title}
</span>
</div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={image}/>
</div>
<div className="price-div">
<span>
£{item.price}
</span>
</div>
<div className="basket-quantity-div">
<button onClick={() => removeItem(item.id)} className="subtract-btn">
<img src={subtractButtonImage}/>
</button>
<span className="quantity-value">
{cartItems.length}
</span>
<button className="add-btn">
<img src={plusButtonImage}/>
</button>
</div>
<div className="total-div">
£{cartItems.reduce((amount, item) => item.price + amount, 0)}
</div>
</div>
)
}
Are you double checked removeItem function?
In the dispatch you are sending { payload: id } and then the reducer get the value from payload.id. If you want to manage the reducer in this way you have to send in the payload an object { id: id } or shorthanded { id }
Related
I am following this tutorial here and followed it exactly, and I have even gone over github repo and code is the same. However when I click my button to add the product to the cart the state does not change. In react dev tools only state that changes is showHideCart changes from false to true - so it seems to only recognise that state change?
I want to be able to add my product to the basket once I click the button - can anyone see where the I have gone wrong? there are no errors in the console and the basket array just does not change it does not even say undefined which was what I thought would of been the case for no product showing up in basket.
Here is a link to a code sandbox
and below is code files where I believe the issues would be:
CartState.js
import { useReducer } from 'react'
import { CartContext } from './CartContext'
import {CartReducer} from './CartReducer'
import { SHOW_HIDE_CART, ADD_TO_CART, REMOVE_ITEM } from '../Types'
export const CartState = ({children}) => {
const initialState ={
showCart: false,
cartItems: [],
};
const [state, dispatch] = useReducer(CartReducer, initialState);
const addToCart = (item) => {
dispatch({type: ADD_TO_CART, payload: item})
};
const showHideCart = () => {
dispatch({type: SHOW_HIDE_CART})
};
const removeItem = (id) => {
dispatch({ type: REMOVE_ITEM, payload: id });
};
return (
<CartContext.Provider
value={{
showCart: state.showCart,
cartItems: state.cartItems,
addToCart,
showHideCart,
removeItem,
}}>
{children}
</CartContext.Provider>
)
};
CartReducer.js
import {ADD_TO_CART, REMOVE_ITEM, SHOW_HIDE_CART } from '../Types'
export const CartReducer = (state, action) => {
switch (action.type) {
case SHOW_HIDE_CART: {
return{
...state,
showCart: !state.showCart
}
}
case ADD_TO_CART: {
return {
...state,
cartItems: [...state.cartItems, action.payload],
}
}
case REMOVE_ITEM: {
return {
...state,
cartItems: state.cartItems.filter(item => item.id !== action.payload)
}
}
default:
return state
}
}
Product.js
import React, { useContext } from 'react'
import { QuantityButtonDiv } from './QuantityButtonDiv'
import {BasketItem} from './BasketItem'
import { CartContext } from '../context/cart/CartContext'
export const Product = ({product}) => {
const {addToCart } = useContext(CartContext)
return (
<div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={product.image}/>
</div>
<div className="details-div">
<h1>{product.title}</h1>
<span>
{product.description}
</span>
<span className="price">
£ {product.price}
</span>
<div className="stock-div">
{product.stock} in stock
</div>
<QuantityButtonDiv/>
<button onClick={() => addToCart(product)} className="button">
Add To Basket
</button>
</div>
</div>
)
}
ProductDetailsPAge.js
import React, { useContext } from 'react';
import '../styles/productDetailsPage.scss';
import img from '../assets/image.png'
import { QuantityButtonDiv } from '../components/QuantityButtonDiv';
import { Product } from '../components/Product';
import { CartContext } from '../context/cart/CartContext';
export const ProductDetailsPage = () => {
const products = [
{
id: 1,
title: "Waxed Cotton Hooded Jacket",
image: require("../assets/image.png"),
description: "The Drumming jacket in orange is finished with a water-repellent dry wax treatment that creates a love-worn look. It's made in the United Kingdom using organic cotton ripstop with a drawstring hood, underarm eyelets and buttoned flap front pockets. Shoulder epaulettes add a utilitarian twist, while a fly-fronted zip and snap-button closure keeps the overall look streamlined. Attach one of the collection's padded liners to the internal tab on cooler days.",
price: 650,
stock: 10,
}
]
return (
<div className="product-details-page">
{
products.map((product) => (
<Product
key={product.id}
product={product}
// title={product.title}
// image={product.image}
// description={item.description}
// price={item.price}
// stock={item.stock}
/>
))
}
</div>
)
}
There is currently an error on basketpage route
You are passing an object through CartContext:
<CartContext.Provider
value={{
showCart: state.showCart,
cartItems: state.cartItems,
addToCart,
showHideCart,
removeItem,
}}>
in BasketPage you are destructuring an array:
const [cart, setCart] = useContext(CartContext);//Typeerror here
Also, you aren't passing neither cart or setCart through that Context value object.
You need to first solve this error and further see if it works alright.
Issue was with showCart and showHideCart - once I got rid of these the cart was working.
I only have one item for my basket app, when I add it multiple times it adds multiple different basketItems all with same id and with same quantity so for example 3 items all of quantity 3. What I want however is for there just to be one item with a quantity of 3.
I also want to be able to remove the item from cart by quantity - so one at a time. however currently when I click to remove from cart it removes it entirely from cart even if quantity in cart is more than 1.
How do I do this?
code sandbox [here] (https://codesandbox.io/s/silly-jepsen-ix3dd?file=/src/pages/ProductDetailsPage.js&resolutionWidth=584&resolutionHeight=696)
code below:
CartReducer.js
import {ADD_TO_CART, CHANGE_CART_QUANTITY, DECREASE, REMOVE_FROM_CART} from '../Types'
export const CartReducer = (state, action) => {
switch (action.type) {
case ADD_TO_CART: {
return {
...state,
cart: [...state.cart, { ...action.payload, qty : 1}]
};
}
case REMOVE_FROM_CART: {
return {
...state,
cart: state.cart.filter((c) => c.id !== action.payload.id,)
};
}
default:
return state
}
}
CartContext.js
import { createContext, useContext, useReducer } from "react";
import { CartReducer } from "./CartReducer";
import { products } from "../../pages/ProductDetailsPage";
const Cart = createContext();
const Context = ({ children }) => {
const [state, dispatch] = useReducer(CartReducer, {
products: products,
cart: [],
});
// const [productState, productDispatch] = useReducer(productReducer, {
// byStock: false,
// byFastDelivery: false,
// byRating: 0,
// searchQuery: "",
// });
// console.log(productState);
return (
<Cart.Provider value={{ state, dispatch }}>
{children}
</Cart.Provider>
);
};
export const CartState = () => {
return useContext(Cart);
};
export default Context;
BasketItem.js
import React, { useContext } from 'react'
import image from '../../assets/image.png'
// import { QuantityButtonDiv } from '../components/QuantityButtonDiv'
import plusButtonImage from '../../assets/vector+.png'
import subtractButtonImage from '../../assets/vector.png'
import { CartState } from '../../context/cart/CartContext'
import { ADD_TO_CART, DECREASE, REMOVE_FROM_CART } from '../../context/Types'
import CustomizedSelect from '../SelectInput'
export const BasketItem = ({item}) => {
// const { cartItems, removeItem } = useContext(CartContext);
const {
state: { cart },
dispatch,
} = CartState();
return (
<div className="basket-item">
<div className="title-div">
<span>
{item.title}
</span>
</div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={image}/>
</div>
<div className="price-div">
<span>
£{item.price}
</span>
</div>
<div className="basket-quantity-div">
<button onClick={() => dispatch({
type: REMOVE_FROM_CART,
payload: item,
})} className="subtract-btn">
<img src={subtractButtonImage}/>
</button>
<span className="quantity-value">
{cart.length}
</span>
<button onClick={() => dispatch({
type: ADD_TO_CART,
payload: item,
})} className="add-btn">
<img src={plusButtonImage}/>
</button>
</div>
<div className="total-div">
£{cart.reduce((amount, item) => item.price + amount, 0)}
</div>
</div>
)
}
Product.js
import React, { useContext, useState } from 'react'
import image from '../../assets/image.png'
import { QuantityButtonDiv } from '../QuantityButtonDiv'
import {BasketItem} from '../basketItem/BasketItem'
import { CartContext, CartState } from '../../context/cart/CartContext'
import { ADD_TO_CART, REMOVE_FROM_CART } from '../../context/Types'
export const Product = ({product}) => {
// const {addToCart, cartItems, removeItem } = useContext(CartContext)
const { state: {cart}, dispatch } = CartState();
const [stockCount, setStockCount] = useState(10)
return (
<div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={image}/>
</div>
<div className="details-div">
<h1>{product.title}</h1>
<span>
{product.description}
</span>
<span className="price">
£ {product.price}
</span>
<div className="stock-div">
{stockCount} in stock
</div>
<QuantityButtonDiv/>
{cart.some((p) => p.id === product.id) ? (
//checking to see if item is in cart if so remove from cart button appears
<button onClick={() => dispatch({
type: REMOVE_FROM_CART,
payload: product,
})} className="remove-button">
Remove From Cart
</button>
) : (
<></>
)}
<button onClick={() => {dispatch({
type: ADD_TO_CART,
payload: product,
}); setStockCount(stockCount-1)}} disable={stockCount <= 0} className="add-to-cart-button">
{stockCount === 0 ? "Out Of Stock" : "Add To Cart"}
</button>
</div>
</div>
)
}
before adding new product to cart, you need to check if the product is already in the cart. You need to check it in ADD_TO_CART case.
in REMOVE_FROM_CART case, check if the quantity is more than 1. If its more than 1, don't remove it.
I am following this tutorial on youtube to implement redux for my ecommerce project. I have followed exactly how the instructor does it however when trying to add a product to the cart I get this error "TypeError: addToCart is not a function".
The only difference between my setup and the tutorial is that I am passing data into my card to display products using props whereas the tutorial renders the product info using redux.
I have looked at many posts online about this error but none of them seem to apply to me as all the potential workarounds I have tried do not works so far.
All relevant code is below.
Card.js
import React, {useState} from 'react';
import 'antd/dist/antd.css';
import { Card, Avatar, Button, Modal } from 'antd';
import { EditOutlined, EllipsisOutlined, PlusCircleTwoTone, SettingOutlined } from '#ant-design/icons';
import {connect} from 'react-redux';
import {addToCart} from '../Redux/Shopping/ShoppingActions'
const { Meta } = Card;
function Cardo(props, {addToCart}) {
//Setting variables up to use for useState so to manage state of modal
//Default state is false so not to be visible
const [isModalVisible, setIsModalVisible] = useState(false);
const showModal = () => {
setIsModalVisible(true);
};
const handleOk = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
//^^^All the const's above will be called below within the card or modal to manage the state of the modal
return (
<div className="card">
<Card
style={{ width: "340px", textAlign: 'center' }}
cover={<img className="card-cover" src={props.image}/>}
actions={[
// <SettingOutlined key="setting" />,
// <EditOutlined onClick={showModal} key="edit" />,
<EllipsisOutlined onClick={showModal} key="ellipsis" />,
]}
>
<Meta
avatar={<Button className="card-button" onClick={() => addToCart(props.id)} type="primary" shape="circle"><PlusCircleTwoTone /></Button>}
title={props.header}
description={props.price}
/>
</Card>
<Modal title={props.header} visible={isModalVisible} onOk={handleOk} onCancel={handleCancel}>
<p>{props.description}</p>
</Modal>
</div>
)
}
const mapDispatchToProps = (dispatch) => {
return{
addToCart: (id) => dispatch(addToCart(id)),
}
}
export default connect(null, mapDispatchToProps)(Cardo)
ShoppingActions.js
import * as actionTypes from './ShoppingTypes';
export const addToCart = (itemID) => {
return{
type: actionTypes.ADD_TO_CART,
payload: {
id: itemID
},
};
};
export const removeFromCart = (itemID) => {
return{
type: actionTypes.REMOVE_FROM_CART,
payload: {
id: itemID
},
};
};
export const adjutQty = (itemID, value) => {
return{
type: actionTypes.ADJUST_QTY,
payload: {
id: itemID,
qty: value,
},
};
};
export const loadCurrentItem = (item) => {
return{
type: actionTypes.LOAD_CURRENT_ITEM,
payload: item,
};
};
ShoppingReducer.js
import * as actionTypes from './ShoppingTypes';
import data from '../../Data/MenuData';
const INITIAL_STATE = {
products: data,//(id, title, description, price, img)
cart: [], //(id, title, description, price, img, qty)
currentItem: null,
}
//reducer is just function that takes in state and action - action is part that gets dispatched which contains a type
const shopReducer = (state = INITIAL_STATE, action) => {
switch(action.type){
case actionTypes.ADD_TO_CART:
//get items data from products array
const item = state.products.find((prod) => prod.id === action.payload.id);
//we need to check if item is in cart already
const inCart = state.cart.find((item) => item.id === action.payload.id ? true : false);
return{
//we spread the state first so not to lose current or all the products
...state,
//inCart we check if it is in cart and that return true - if so map through cart and find that id
cart: inCart ? state.cart.map((item) =>
item.id === action.payload.id
//Then spread all of data inside and change quantity if needed
? {...item, qty: item.quantity + 1} : item
) //if not in cart then spread the array and add the item and quantity to state of cart
: [...state.cart, { ...item, qty: 1}],
};
case actionTypes.REMOVE_FROM_CART:
return{
...state,
//this filters through array and deletes item we want to remove
cart: state.cart.filter(item => item.id !== action.payload.id)
};
case actionTypes.ADJUST_QTY:
return{
...state,
//if i find id in cart I want to recreate object by spreading current item and setting qty set to original qty - else return item
cart: state.cart.map((item) => item.id === action.payload.id ? {...item, qty: action.payload.qty} : item)
};
case actionTypes.LOAD_CURRENT_ITEM:
return{
...state,
currentItem: action.payload,
};
default:
return state;
}
}
export default shopReducer;
function Cardo(props, {addToCart}) {
here lies the error addToCart is a property of props, so it should look like this
function Cardo(props) {
const {addToCart} = props
This is not how you take props
function Cardo(props, {addToCart})
If you want to make all props you just simply make
function Cardo(props)
and then use props.addToCart
but if you want to do not use props.addToCart you can make it:
function Cardo(props: { addToCart })
so now all items that you pass into {} like { addToCart, anotherProp, thirdProp }
will be from props
you can also use this way:
function Cardo(props)
and under it:
const { addToCart, anotherProp, thirdProp } = props;
and then just use normal addToCart
I'm working on a budget tracking app, where the user can add different incomes and expenses. I'm using useReducer to manage state. Each income and expense is an object and when the user submits an income or expense, it is displayed as an income list with income items or expense list with expense items. Each item in the list has a remove button that should remove the income item or expense item. But when I try click the remove button to remove an income item for example, I get an error saying list.map is not a function.
Not exactly sure why this happens, but I think it has something to do with the removeItem function in IncomeOutputList.js. What would be the proper way to remove an income item?
App.js
import React, { useState, useReducer } from 'react';
import './App.css';
import BudgetInput from './components/input/BudgetInput';
import BudgetOutput from './components/output/BudgetOutput';
import IncomeOutputList from './components/output/IncomeOutputList';
import ExpenseOutputList from './components/output/ExpenseOutputList';
const useSemiPersistentState = (key, initialState) => {
// console.log(JSON.parse(localStorage.getItem(key)));
const [value, setValue] = React.useState(
localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState
);
React.useEffect(()=>{
localStorage.setItem(key, JSON.stringify(value));
}, [value, key])
return [value, setValue];
};
const initialBudget = {
description: '',
type: '+',
key: 'income',
value: ''
};
const initialState = {
incomes: [{}],
expenses: [{}],
budgetObj: initialBudget
};
const budgetReducer = (state, action) => {
switch(action.type) {
case "CHECK_STATE":
console.log(state); //just to check state on submit
case "ON_DESC_CHANGE":
return {
...state,
budgetObj: {
...state.budgetObj,
description: action.payload
}
}
case "ON_TYPE_CHANGE":
const isExpense = action.payload === '-';
return {
...state,
budgetObj: {
...state.budgetObj,
type: action.payload,
key: isExpense ? 'expense' : 'income'
}
}
case "ON_VALUE_CHANGE":
return {
...state,
budgetObj: {
...state.budgetObj,
value: action.payload,
}
}
case 'SUBMIT_BUDGET':
console.log(state.incomes);
const budget = {...state};
const isIncome = budget.budgetObj.type === '+';
return {
incomes: isIncome ? state.incomes.concat(budget.budgetObj) : state.incomes,
expenses: isIncome ? state.expenses : state.expenses.concat(budget.budgetObj),
budgetObj: initialBudget,
}
case "REMOVE_ITEM":
console.log('test');
return {
...state,
// not sure if the following line is the correct way to remove from local storage
incomes: (index) => JSON.parse(localStorage.getItem("income")).splice(index,1),
// expenses: (index) => JSON.parse(localStorage.getItem("expense")).splice(index,1)
}
default:
return state;
}
}
const useSemiPersistantReducer = (key, initialState) => {
const [value, dispatch] = React.useReducer(
budgetReducer,
localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState
);
React.useEffect(()=>{
localStorage.setItem(key, JSON.stringify(value));
}, [value, dispatch]) //[inocmes, setIncomes]
return [value, dispatch];
}
const App = () => {
const [budgetState, setBudget] = useSemiPersistantReducer(initialState.budgetObj.key,initialState);
const {incomes, expenses, budgetObj, key} = budgetState;
return (
<div className="App">
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css"></link>
<div className="top">
<BudgetOutput />
</div>
<div className="bottom">
<BudgetInput
descValue={budgetObj.description || ''} //event.target.value
onDescChange={event => setBudget({ type: "ON_DESC_CHANGE", payload: event.target.value })}
onSelectChange={event => setBudget({ type: "ON_TYPE_CHANGE", payload: event.target.value })}
type={budgetObj.type || ''}
onBudgetSubmit={ () => setBudget({ type : 'SUBMIT_BUDGET' }) }
budgetValue={budgetObj.value || ''}
onValChange={event => setBudget({ type: "ON_VALUE_CHANGE", payload: event.target.value })}
/>
<div className="container clearfix">
<IncomeOutputList
list={incomes}
removeIncome={ () => setBudget({ type: "REMOVE_ITEM" })} //not sure if this is correct?
/>
<ExpenseOutputList
list={expenses}
// removeExpense={(index)=>removeExp(index)}
/>
</div>
</div>
</div>
)
};
export default App;
IncomeOutput.js
import React from 'react';
import IncomeOutput from './IncomeOutput';
// list will be list of income objects
const IncomeOutputList = ({ list, removeIncome }) => {
return (
<div className="income__list">
<div className="income__list--title">INCOME</div>
{list.map((item, index, arr) => <IncomeOutput
id={item.id}
value={item.value}
type={item.type}
desc={item.description}
// error has something to do with the following line?
handleButton={()=>removeIncome(index)}
/>
)}
</div>
)
}
export default IncomeOutputList;
IncomeOutput.js
import React from 'react';
import ValueOutput from './ValueOutput';
const IncomeOutput = ({ desc, type,id, value, handleButton }) => {
return (
<>
<div className="item clearfix income" id={id}>
<div className="item__description">{desc}</div>
<ValueOutput
type={type}
value={value}
handleClick={handleButton}
/>
</div>
</>
)
}
export default IncomeOutput;
ValueOutput.js
import React from 'react';
import Button from '../buttons/Button';
const ValueOutput = ({type, value, handleClick}) => {
return (
<>
<div className="right clearfix">
<div className="item__value">{type} {value}</div>
<Button buttonType="item__delete--btn" handler={handleClick}/>
</div>
</>
)
}
export default ValueOutput;
Button.js
import React from 'react';
const Button = ({buttonType, handler}) => (
<>
<div className="item__delete">
<button className={buttonType} onClick={handler}>
<i className="ion-ios-close-outline"></i>
</button>
</div>
</>
)
export default Button;
I didn't run your code but just a guess
incomes: (index) => JSON.parse(localStorage.getItem("income")).splice(index,1),
incomes is a function not an array, so "incomes.map()" does not exist.
I am new to to redux and react. Still doing simple tutorials. I managed to create 2 simple components; one that outputs on the screen (as a list) whatever is in the array in the redux store, and the other component contains a button and a textfield which basically adds to that array in the store.
I would like to add a feature that will enable me to delete a specific entry in the list depending on what the user clicked on. I am thinking of creating a <button> next to each <li> tag that gets rendered as it loops through the array, and these buttons will correspond to the respective list elements. But I'm not sure how to do that.
I've tried creating a button when each <li> tag gets created but I was getting an error on the console stating that each element in a list needs a unique ID. I then decided to create another array in my store called buttons which will contain a unique id as well as the id of the list but it got out of hand. I think I might be overcomplicating this. This is what I have at the moment:
Components:
List.jsx (responsible for outputting the list)
import React from 'react'
import { connect } from "react-redux";
const ListComp = ({ lists }) => (
<div>
<ul>
{console.log(lists)}
{lists.map( element => (
<li key={element.id}>
{element.titleToBeAddedToList}
</li>
))}
</ul>
</div>
)
const mapStateToProps = state => {
return {
lists: state.lists
};
}
const List = connect(mapStateToProps)(ListComp)
export default List;
SubmitButton.jsx (responsible for outputting the button and textfield)
import React from 'react'
import { connect } from "react-redux";
import uuidv1 from "uuid";
import { addList } from "../actions/index";
import { addButton } from "../actions/index"
function mapDispatchToProps(dispatch){
return {
addlist: article => dispatch(addList(article)),
addbutton: idOfButton => dispatch(addButton(idOfButton))
};
}
class Submit extends React.Component{
constructor(){
super();
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
const {titleToBeAddedToList} = this.state;
const id = uuidv1();
const button_id = uuidv1();
//Dispatching the action:
this.props.addlist({ titleToBeAddedToList, id });
this.props.addbutton({id, button_id});
//Once we've dispatched an action, we want to clear the state:
this.setState({ titleToBeAddedToList: "" });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="title">Title</label>
<input
type="text"
className="form-control"
id="titleToBeAddedToList"
onChange={this.handleChange}
/>
</div>
<button type="submit" className="btn btn-success btn-lg">
SAVE
</button>
</form>
);
}
}
const SubmitButton = connect(null, mapDispatchToProps)(Submit)
export default SubmitButton;
Reducers:
const initialState = {
lists: [],
buttons: []
};
function rootReducer (state = initialState, action) {
if(action.type === "ADD_LIST" ){
return Object.assign({}, state, {
lists: state.lists.concat(action.payload)
});
} else if(action.type === "ADD_BUTTON"){
return Object.assign({}, state, {
buttons: state.lists.concat(action.payload)
});
} else if(action.type === "DELETE_FROM_LIST"){
//.....//
}
return state;
}
export default rootReducer;
Action:
export function addList(payload) {
return { type: "ADD_LIST", payload }
};
export function addButton(payload){
return {type: "ADD_BUTTON", payload }
}
export function deleteList(payload){
return { type: "DELETE_FROM_LIST", payload }
}
Store:
import { createStore } from "redux";
import rootReducer from "../reducers/index";
const store = createStore(rootReducer);
export default store;
You can use Math.random() as an unique key identifier, if the button is click it will call action deleteItem with the ID, action is bound to reducer pass on the ID, you can then use the ID to indentify elements and remove it in the list.
import React from 'react'
import { connect } from "react-redux";
import { deleteItem } from './actions';
const ListComp = ({ lists }) => (
<div>
<ul>
{console.log(lists)}
{lists.map( element => (
<li key={Math.random()} key={element.id}>
{element.titleToBeAddedToList}
<button onClick={() => deleteItem(element.id)}>X</button>
</li>
))}
</ul>
</div>
)
const mapStateToProps = state => {
return {
lists: state.lists
};
}
const List = connect(mapStateToProps, {deleteItem})(ListComp) // Make it available to component as props
export default List;
Action:
export function deleteElement(id) {
return function(dispatch) {
return dispatch({type: "DELETE_FROM_LIST", payload: id})
}
}
Reducer:
case 'DELETE_FROM_LIST': {
const id = action.payload;
return {
...state,
list: state.list.filter(item => item.id !== id)
}
}
else if (action.type === "DELETE_FROM_LIST") {
return Object.assign({}, state, {
buttons: state.lists.filter(item => (item.id !==action.payload))
});
}
you can use filter() for delete.
This is a minimal working react-redux example containing all the pieces to delete an item from an array in redux store.
// reducer.js
const reducer = (state, action) => {
switch (action.type) {
case 'DELETE':
return state.filter(item => (
item.id !== action.payload.id
))
default: return state;
}
}
// Item.js
const Item = ({id, onClick, label}) => (
<li>
{label}
<button onClick={ () => onClick(id) }>
delete
</button>
</li>
)
// ListContainer.js
const mapStateToProps = state => ({ items: state })
const ListContainer = ReactRedux.connect(mapStateToProps)(class extends React.Component {
handleDelete = id => {
const { dispatch } = this.props;
dispatch({ type: 'DELETE', payload: { id } })
}
render() {
const { items } = this.props;
return items.map(({id, label}) => (
<Item
label={label}
id={id}
onClick={this.handleDelete}
/>
))
}
})
// Main.js
const initialState = [
{ id: 1, label: 'item 1' },
{ id: 2, label: 'item 2' },
{ id: 3, label: 'item 3' },
{ id: 4, label: 'item 4' }
]
const store = Redux.createStore(reducer, initialState);
class App extends React.Component {
render(){
return (
<ReactRedux.Provider store={store}>
<ListContainer />
</ReactRedux.Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/6.0.1/react-redux.js"></script>
<div id="root"></div>