React - Redux-Toolkit items in cart - reactjs

I am learning react with redux-toolkit. I am stuck with some actions there.
I want to add quantity in Cart, so if I add same item more than once it should be like X1/x2/x3...etc.
And I want to delete items/item but only with the same ID ( when I click delete only delete that one ex. APPLE)
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
hidden: true,
cartItems: 0,
itemsInCart: [],
quantity: 0,
totalCount: 0,
};
export const cartSlice = createSlice({
name: "cart",
initialState,
reducers: {
removeItem: (state, action) => {},
removeAll: (state) => {
state.cartItems = 0;
state.itemsInCart = [];
state.totalCount = 0;
},
addToCart(state, action) {
state.itemsInCar = state.itemsInCart.push(action.payload);
state.cartItems += 1;
state.totalCount += action.payload.price;
},
showCart: (state) => {
state.hidden = !state.hidden;
},
},
});
export const { showCart, addToCart, removeAll, removeItem } = cartSlice.actions;
export default cartSlice.reducer;

addToCart: (state, action) => {
const itemInCart = state.cart.find((item) => item.id === action.payload.id);
if (itemInCart) {
itemInCart.quantity++;
} else {
state.cart.push({ ...action.payload, quantity: 1 });
}
},

Related

Can't fetchdata with useEffect in a product component

hello everyone I am aking a MERN project and Im running into a peobleme.
so Im using redux toolkit slice and when I try to dispatch the fetchProduct like so
import React, { useEffect, useState } from "react";
import "./products.css";
import { useSelector, useDispatch } from "react-redux";
import { DataGrid } from "#mui/x-data-grid";
import { Link, Navigate } from "react-router-dom";
import DeleteForeverIcon from "#mui/icons-material/DeleteForever";
import Loader from "components/Loader";
import { Button } from "#mui/material";
import { useNavigate } from "react-router-dom";
import Message from "components/Message";
import { fetchProducts } from "slices/productSlice";
const Products = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { loading, error, products } = useSelector((state) => state.product);
useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
const columns = [
{ field: "_id", hide: true },
{
field: "name",
flex: 1,
headerName: "Product",
width: 200,
renderCell: (params) => {
return (
<div className="productListItem">
<img className="productListImg" src={params.row.image} alt="" />
{params.row.name}
</div>
);
},
},
{ field: "countInStock", headerName: "Stock", flex: 1 },
{
field: "price",
headerName: "Price",
flex: 1,
},
{
field: "brand",
headerName: "Brand",
flex: 1,
},
{
field: "action",
headerName: "Action",
flex: 1,
renderCell: (params) => {
return (
<>
<Link to={"/products/" + params.row._id}>
<button className="productListEdit">Edit</button>
</Link>
<DeleteForeverIcon
className="productListDelete"
// onClick={() => deleteHandler(params.row._id)}
/>
</>
);
},
},
];
return (
<div style={{ height: "90vh" }}>
{loading ? (
<Loader />
) : error ? (
<Message variant="error" />
) : (
<DataGrid
height={100}
getRowId={(row) => row._id}
rows={products?.products}
disableSelectionOnClick
columns={columns}
rowsPerPageOptions={[10, 15, 20]}
pageSize={10}
checkboxSelection
/>
)}
</div>
);
};
export default Products;
the data never get fetched and I get this error in the browser:
"Warning: Failed prop type: The prop rows is marked as required in ForwardRef(DataGrid), but its value is undefined."
but when I remove what's inside the return of the componenet and add just an h1 tag the data get fetched and I can find it in redux store states
this is the slice I am working with :
import axios from 'axios';
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
products: [],
loading: false,
error: null
};
const baseurl = "http://localhost:5001"
const productSlice = createSlice({
name: 'product',
initialState,
reducers: {
fetchProductsStart(state) {
state.loading = true;
},
fetchProductsSuccess(state, action) {
state.products = action.payload;
state.loading = false;
state.error = null;
},
fetchProductsError(state, action) {
state.loading = false;
state.error = action.payload;
},
deleteProductStart(state) {
state.loading = true;
},
deleteProductSuccess(state, action) {
state.products = state.products.filter(product => product._id !== action.payload);
state.loading = false;
state.error = null;
},
deleteProductError(state, action) {
state.loading = false;
state.error = action.payload;
},
updateProductStart(state) {
state.loading = true;
},
updateProductSuccess(state, action) {
const index = state.products.findIndex(product => product._id === action.payload._id);
state.products[index] = action.payload;
state.loading = false;
state.error = null;
},
updateProductError(state, action) {
state.loading = false;
state.error = action.payload;
},
fetchProductStart(state) {
state.loading = true;
},
fetchProductSuccess(state, action) {
state.products = [action.payload];
state.loading = false;
state.error = null;
},
fetchProductError(state, action) {
state.loading = false;
state.error = action.payload;
}
}
});
export const {
fetchProductsStart,
fetchProductsSuccess,
fetchProductsError,
deleteProductStart,
deleteProductSuccess,
deleteProductError,
updateProductStart,
updateProductSuccess,
updateProductError,
fetchProductStart,
fetchProductSuccess,
fetchProductError
} = productSlice.actions;
export default productSlice.reducer
export const fetchProducts = () => async dispatch => {
try {
dispatch(fetchProductsStart());
const response = await axios.get(`${baseurl}/products`);
dispatch(fetchProductsSuccess(response.data));
} catch (error) {
dispatch(fetchProductsError(error.message));
}
};
export const deleteProduct = id => async dispatch => {
try {
dispatch(deleteProductStart());
await axios.delete(`${baseurl}/products/${id}`);
dispatch(deleteProductSuccess(id));
} catch (error) {
dispatch(deleteProductError(error.message));
}
};
export const updateProduct = product => async dispatch => {
try {
dispatch(updateProductStart());
const response = await axios.patch(`${baseurl}/products/${product._id}`, product);
dispatch(updateProductSuccess(response.data));
} catch (error) {
dispatch(updateProductError(error.message));
}
};
so what am I doing wrrong here
the modified Slice
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
const initialState = {
products: [],
loading: 'idle',
error: null
};
const baseurl = 'http://localhost:5001';
export const fetchProducts = createAsyncThunk(
'products/fetchProducts',
async () => {
const response = await axios.get(`${baseurl}/products`);
return response.data;
}
);
export const deleteProduct = createAsyncThunk(
'products/deleteProduct',
async id => {
await axios.delete(`${baseurl}/products/${id}`);
return id;
}
);
export const updateProduct = createAsyncThunk(
'products/updateProduct',
async product => {
const response = await axios.patch(
`${baseurl}/products/${product._id}`,
product
);
return response.data;
}
);
export const fetchProduct = createAsyncThunk(
'products/fetchProduct',
async id => {
const response = await axios.get(`${baseurl}/products/${id}`);
return response.data;
}
)
const productSlice = createSlice({
name: 'products',
initialState,
reducers: {},
extraReducers: {
[fetchProducts.pending]: state => {
state.loading = 'pending';
},
[fetchProducts.fulfilled]: (state, action) => {
state.loading = 'idle';
state.products = action.payload;
},
[fetchProducts.rejected]: (state, action) => {
state.loading = 'idle';
state.error = action.error.message;
},
[deleteProduct.pending]: state => {
state.loading = 'pending';
},
[deleteProduct.fulfilled]: (state, action) => {
state.loading = 'idle';
state.products = state.products.filter(product => product._id !== action.payload);
},
[deleteProduct.rejected]: (state, action) => {
state.loading = 'idle';
state.error = action.error.message;
},
[updateProduct.pending]: state => {
state.loading = 'pending';
},
[updateProduct.fulfilled]: (state, action) => {
state.loading = 'idle';
const index = state.products.findIndex(product => product._id === action.payload._id);
state.products[index] = action.payload;
},
[updateProduct.rejected]: (state, action) => {
state.loading = 'idle';
state.error = action.error.message

Best way to share state between slices in redux toolkit?

If I have a slice like this
cartSlice.js
import { createSlice } from "#reduxjs/toolkit";
import cartItems from "../cartItems";
const initialState = {
cartItems: cartItems,
amount: 1,
total: 0,
isLoading: true,
};
export const cartSlice = createSlice({
name: "cart",
initialState,
reducers: {
clearCart: (store) => {
store.cartItems = [];
},
removeItem: (store, action) => {
store.cartItems = store.cartItems.filter(
(item) => item.id !== action.payload
);
},
increase: (store, action) => {
const target_item = store.cartItems.find(
(item) => item.id === action.payload.id
);
target_item.amount++;
},
decrease: (store, { payload }) => {
const target_item = store.cartItems.find(
(item) => item.id === payload.id
);
target_item.amount--;
},
calculate: (store) => {
let amount = 0;
let total = 0;
store.cartItems.forEach((item) => {
amount += item.amount;
total += item.amount * item.price;
});
store.amount = amount;
store.total = total;
},
},
});
export const { clearCart, removeItem, increase, decrease, calculate } =
cartSlice.actions;
export default cartSlice.reducer;
How do I implement something like this?
checkoutSlice.js
const initialState = {
purchasedItems: [],
checkoutIsOpen: false,
};
const { cartItems, amount } = useSelector((store) => store.cart); <--HYPOTHETICALLY
const checkoutSlice = createSlice({
name: "checkout",
initialState,
reducers: {
addToCheckout: (state) => {
if (amount >= 1) { <------HERE
state.purchasedItems.push(cartItems); <---HERE
}
},
openCheckout: (state) => {
state.checkoutIsOpen = true;
},
},
});
export const { addToCheckout, openCheckout } = checkoutSlice.actions;
export default checkoutSlice.reducer;
You can't use selectors, what else is left? I've read a ton of previous posts that say it's not possible, but then how do you create functional websites with components that interact with each other? Like in this case with basic shopping app with a checkout cart, how do you get the selected items into the checkout cart? It's just not possible? That doesn't make sense because isn't that basic core functionality of a website? Why wouldn't redux allow this? I feel like there has to be a way.
I think I'm fundamentally misunderstanding here.
Any help? How do I accomplish this?

React redux doesn't automatically render the changed things

I have a problem with React redux. And I want to display the current state immediately. Unfortunately this doesn't work as intended. The changed data is only displayed correctly after the page has been reloaded.
This is the Main Part in my Articel.js
const buySomething = async (articelId) => {
await axios.put(`/articel/request/${articelId}`).then((res) => {
dispatch(requested(currentUser._id));
setSnackbarMessage(res.data);
setOpen(true);
});
};
Articel model:
requested: {
type: [String],
default: [],
},
articelSlice.js
const initialState = {
currentArticel: null,
loading: false,
error: false,
};
requested: (state, action) => {
if (!state.currentArticel.requested.includes(action.payload)) {
state.currentArticel.requested.push(action.payload);
} else {
state.currentArticel.requested.splice(
state.currentArticel.requested.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
Complete articelSlice Code:
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
currentArticel: null,
loading: false,
error: false,
};
export const articelSlice = createSlice({
name: "articel",
initialState,
reducers: {
fetchStart: (state) => {
state.loading = true;
},
fetchSuccess: (state, action) => {
state.loading = false;
state.currentArticel = action.payload;
},
fetchFailure: (state) => {
state.loading = false;
state.error = true;
},
requested: (state, action) => {
if (!state.currentArticel.requested.includes(action.payload)) {
state.currentArticel.requested.push(action.payload);
} else {
state.currentArticel.requested.splice(
state.currentArticel.requested.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
like: (state, action) => {
if (!state.currentArticel.likes.includes(action.payload)) {
state.currentArticel.likes.push(action.payload);
state.currentArticel.dislikes.splice(
state.currentArticel.dislikes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
dislike: (state, action) => {
if (!state.currentArticel.dislikes.includes(action.payload)) {
state.currentArticel.dislikes.push(action.payload);
state.currentArticel.likes.splice(
state.currentArticel.likes.findIndex(
(userId) => userId === action.payload
),
1
);
}
},
},
});
export const {
fetchStart,
fetchSuccess,
fetchFailure,
like,
dislike,
requested,
} = articelSlice.actions;
export default articelSlice.reducer;

Add to cart in redux-toolkit (state undefined), Someone, please help me?

I am trying to add a product to the cart.
I don't understand why the state is undefined,
I have a localStorage 'cart' :
(3) [{…}, {…}, {…}]
0: {product: {…}, quantity: 1}
1: {product: {…}, quantity: 2}
console.log(action.payload):
{product: {…}, quantity: 1}
When I clicked the add cart button, my localStorage added correctly but the state was automatically lost
My code cartSlice.js:
import { createSlice } from '#reduxjs/toolkit';
import {
createCart,
getCart,
updateCart,
deleteCart
} from './../asyncActions/cart.asyncAction';
var data = JSON.parse(localStorage.getItem('cart'));
const cartSlice = createSlice({
name: 'cart',
initialState: {
cart: data ? data : [],
searchValue: '',
},
reducers: {
},
extraReducers: {
//* get cart
[getCart.pending]: (state, action) => {
},
[getCart.fulfilled]: (state, action) => {
if (action.payload) {
state.cart = action.payload;
}
},
[getCart.rejected]: (state, action) => {
},
// create
[createCart.pending]: (state, action) => {
},
[createCart.fulfilled]: (state, action) => {
if (action.payload) {
let idProductAction = action.payload.product.id;
var index = state.cart ? state.cart.map((item) => item.product.id).indexOf(idProductAction) : -1;
if(index !== -1){
state.cart[index].quantity += action.payload.quantity;
} else {
state.cart = [action.payload, ...state.cart];
}
state.cart = localStorage.setItem('cart', JSON.stringify(state.cart));
}
},
[createCart.rejected]: (state, action) => {
console.log('sai');
},
}
});
const { actions, reducer } = cartSlice;
const { clearStateCart } = actions;
export { clearStateCart };
export default reducer;
My code component Cart.js:
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getCart, createCart } from '../../store/asyncActions/cart.asyncAction';
function Cart(props) {
const classes = useStyles();
const dispatch = useDispatch();
let cart = useSelector((state) => state.cart.cart);
let productList = useSelector((state) => state.products.products);
const addCart = (product) => {
getDispatchCreateCart(product);
}
const getDispatchCreateCart = (product) => {
dispatch (
createCart({
product: product,
quantity: 1
})
)
}
const getDispatchProducts = () => {
dispatch (
getProducts()
)
}
const getDispatchGetCart = () => {
dispatch (
getCart()
)
}
useEffect(() => {
getDispatchProducts();
getDispatchGetCart();
}, []);
return (...);
}
export default Cart;
my redux devtools show:
cart/createCart/pedding : state cart has data.
enter image description here
cart/createCart/fulfilled : state cart undefined.
enter image description here
I don't understand why the state is undefined,
Hope everybody help please.
You can not state.cart, It will be lost by localStorage typeof undefined:
state.cart = localStorage.setItem('cart', JSON.stringify(state.cart));
Edit:
[createCart.fulfilled]: (state, action) => {
if (action.payload) {
let idProductAction = action.payload.product.id;
var index = state.cart ? state.cart.map((item) => item.product.id).indexOf(idProductAction) : -1;
if(index !== -1){
state.cart[index].quantity += action.payload.quantity;
} else {
state.cart = [action.payload, ...state.cart];
}
localStorage.setItem('cart', JSON.stringify(state.cart));
}
},

Adding existing item to Cart (React-Redux)

I'm using React and Redux for creating shop. I need to add existing item to cart with increasing quantity.
I know, that Redux is based on immutability, but I can't find how to get rid of mutation. Please, give me a hint.
So, there is my code
Actions:
export const UPDATE_QTY = 'UPDATE_QTY';
export const UPDATE_CART = 'UPDATE_CART';
Reducer and initialState:
const initialState = {
cart: [],
qty: 0,
total: 0,
delivery: 5,
};
export const cartReducer = (state = initialState, action) => {
switch (action.type) {
case actions.UPDATE_QTY:
let existedItem = state.cart.filter((cartItem) => cartItem.id === action.payload.id);
existedItem[0].qty = action.payload.qty;
return {
...state,
qty // how to get rid of mutation here?
};
case actions.UPDATE_CART:
return { ...state, cart:[...state.cart, action.payload] };
default:
return state;
}
};
And my Component with dispatch:
export default function AddBtn({ id }) {
const itemData = useSelector((state) => state.main.items);
const cartData = useSelector((state) => state.app.cart);
const dispatch = useDispatch();
const handleAddToCart = () => {
const addedItem = itemData.find((item) => item.id === id);
const existedItem = cartData.find((item) => id === item.id);
if (existedItem) {
dispatch({
type: UPDATE_QTY,
payload: { id, qty: existedItem.qty + 1 },
});
} else {
dispatch({
type: UPDATE_CART,
payload: addedItem,
});
}
return (
// JSX code
)
You can use map function instead, which are immutable:
const initialState = {
cart: [],
qty: 0,
total: 0,
delivery: 5,
};
export const cartReducer = (state = initialState, action) => {
switch (action.type) {
case actions.UPDATE_QTY:
return {
...state,
cart: state.cart.map(el => {
if (el.id === action.payload.id) {
return {
...el,
qty: action.payload.qty
}
}
return el;
})
};
case actions.UPDATE_CART:
return { ...state,
cart: [...state.cart, action.payload]
};
default:
return state;
}
};
You can try this:
const initialState = {
cart: [],
qty: 0,
total: 0,
delivery: 5,
};
export const cartReducer = (state = initialState, action) => {
switch (action.type) {
case actions.UPDATE_QTY:
let existedItem = state.cart.find((cartItem) => cartItem.id === action.payload.id);
if(existedItem){
existedItem.qty = action.payload.qty;
}
return {
...state,
cart:[...state.cart] // how to get rid of mutation here?
};
case actions.UPDATE_CART:
return { ...state, cart:[...state.cart, action.payload] };
default:
return state;
}
};

Resources