Adding existing item to Cart (React-Redux) - reactjs

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;
}
};

Related

React - Redux-Toolkit items in cart

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 });
}
},

Redux initial state from API issue

My issue is that I want to fetch all products from the database and set them into the Redux initial state, to do this I did an action SET_PRODUCTS_LIST and in the action. payload I simply passed the products fetched in the component (I am using next js), all works fine but when I try to fire another action like ADD_PRODUCT_TO_CART the products in the initial state are gone which it results impossible to add more than 1 product to the cart.
Inside my component:
function Header({ cartProps, setProducts }) {
useEffect(async () => {
const products = await getProducts();
setProducts(products);
}, []);
}
const mapStateToProps = (state) => {
return {
cartProps: state.cartState,
};
};
export default connect(mapStateToProps, {
setProducts,
})(Header);
the action to set products:
import { SET_PRODUCTS_LIST } from "./types";
export const setProducts = (products) => {
return (dispatch) => {
dispatch({
type: SET_PRODUCTS_LIST,
payload: products,
});
};
};
My cart reducer:
const initialState = {
itemNumbers: 0,
cartCost: 0,
products: [],
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_PRODUCTS_LIST: {
return {
...state,
products: action.payload,
};
}
case ADD_PRODUCT_TO_CART: {
//let addQuantity = {
// ...state.products.filter((p) => p.productName === action.paylaod),
// };
console.log(state.products);
return {
itemNumbers: state.itemNumbers + 1,
};
}
default:
return state;
}
};
export default reducer;
maybe I am completely doing wrong the logic about fetching the products in order to have them in the initial state.
const initialState = {
itemNumbers: 0,
cartCost: 0,
products: [],
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_PRODUCTS_LIST: {
return {
...state,
products: action.payload,
};
}
case ADD_PRODUCT_TO_CART: {
//let addQuantity = {
// ...state.products.filter((p) => p.productName === action.paylaod),
// };
console.log(state.products);
return {
...state
itemNumbers: state.itemNumbers + 1,
};
}
default:
return state;
}
};
export default reducer;
You should always return state but in ADD_PRODUCT_TO_CART case you return only
{
itemNumbers: state.itemNumbers + 1,
}
so you need to add ...state before itemNumbers: state.itemNumbers + 1,

Load a Spinner in React Redux (Thunk)

I have a redux store as follows:
const initState = {
data: {},
isFetching: false,
};
I have a async function to fetch the data for the state:
const requestSideBarData = async(state, actions) => {
const request = actions.request
let newState = state;
const promises = [];
Object.keys(newState.data).forEach(element => {
promises.push(axios.post(`${ApiEndPoints.getDropDownHeaders}${element}`,request))
});
const results = await Promise.all(promises);
var index = 0;
Object.keys(newState.data).forEach(element => {
newState.data[element] = results[index++].data;
})
return newState;
};
And here are my begin and end reducers:
const fetchSideBarDataBegin = (state) => {
return {
...state,
isFetching: true,
}
};
const fetchSideBarDataEnd = (state) => {
return {
...state,
isFetching: false,
}
};
here's my actions function:
export const fetchSidebBatData = (dateData,selections) => {
return {
type: actions.FETCH_SIDEBAR_DATA_BEGIN, //Here there should be some combine action
dateData: dateData,
selections: selections,
requests: {...dateData,...selections}
}
};
And finally here's my reducer:
const reducer = ( state = initState, action ) => {
switch ( action.type ) {
case actionTypes.FETCH_SIDEBAR_DATA_REQUEST: return fetchSideBarData(state, action);
case actionTypes.FETCH_SIDEBAR_DATA_BEGIN: return fetchSideBarDataBegin(state);
case actionTypes.FETCH_SIDEBAR_DATA_END: return fetchSideBarDataEnd(state);
default: return state;
}
};
What I intend to do is to combine these three reducers into 1: so basically:
FETCH_SIDEBAR_DATA_BEGIN
FETCH_SIDEBAR_DATA_REQUEST
and finally
FETCH_SIDEBAR_DATA_END
what is the right way to perform this operation?

how to pass products id with redux count in react native for ecommerce like addtocart function

making an eCommerce like platform and here I want to add_to_cart and show the count on the Icon. and and with count show the product as per the id which stored in array
this is my reducers :-
const cartItems = (state = [], action) => {
switch (action.type) {
case 'ADD_TO_CART':
return [...state, action.payload]
case 'REMOVE_FROM_CART':
return state.filter(cartItem => cartItem.id !== action.payload.id)
}
return state
}
export default cartItems
in which i am increasing the count as per the item seleted
const mapStateToProps = reduxStore => {
return {
cartItems: reduxStore
}
}
const mapDispatchToProps = (dispatch) => {
return {
addItemToCart: (product) => dispatch({ type: 'ADD_TO_CART', payload: product })
}
}
export default connect(mapStateToProps , mapDispatchToProps)(ContentPage)
only I am getting the count not the cart items and I want to pass this.state.data.name && this.state.data.img which getting from the URL!
For passing in additional props if that is what you are asking, mapStateToProps also takes an optional props
const mapStateToProps = (reduxStore, ...otherProps) => {
return {
cartItems: reduxStore
}
}
you can reference the data through this.props.cartItems
(Edited) Try updating the state like this:
const initialState = {
cartItems: [],
};
const cartItems = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_CART':
return { ...state, cartItems: state.cartItems.push(action.payload) };
case 'REMOVE_FROM_CART':
return {
...state,
cartItems: state.filter(cartItem => cartItem.id !== action.payload.id),
};
return state;
}
};
And to have access to cartItems in the props:
const mapStateToProps = reduxStore => {
return {
cartItems: reduxStore.cartItems
}
}

ReactJS modifiying data in state

I just starting on React, and starting to do a todo list. It has functionalities like add, modify(done/pending) and remove task.
Below is my action
export const ADD_TASK = 'ADD_TASK';
export const TOGGLE_TASK = 'TOGGLE_TASK';
export const REMOVE_TASK = 'REMOVE_TASK';
export const FILTER_TASK = 'FILTER_TASK';
let todoId = 1;
export function addTask(task) {
let todo = {
id: todoId++,
name: task,
status: 0,
visible: true
};
return {
type: ADD_TASK,
payload: todo
};
}
export function toggleTask(id) {
return {
type: TOGGLE_TASK,
payload: id
};
}
export function removeTask(id) {
return {
type: REMOVE_TASK,
payload: id
};
}
export function filterTask(id) {
return {
type: FILTER_TASK,
payload: id
};
}
and my reducer :
import { ADD_TASK, TOGGLE_TASK, REMOVE_TASK, FILTER_TASK } from '../actions/index';
let filterStatus = -1;
//initial state is array because we want list of city weather data
export default function(state = [], action) {
// console.log('Action received', action);
const toggling = function (t, action) {
if(t.id !== action)
return t;
return Object.assign({}, t, {
status: !t.status
})
};
const visibility = function(t, action) {
return Object.assign({}, t, {
visible: action === -1 ? true : t.status == action
})
};
switch(action.type) {
case ADD_TASK :
//return state.concat([ action.payload.data ]); //in redux reducer dont modify the state, instead create a new one baesd on old one. Here concat is create a new of old one and add a new data
return [ action.payload, ...state];
case TOGGLE_TASK :
return state.map(s => toggling(s, action.payload)).map(t => visibility(t, filterStatus));
case REMOVE_TASK :
return state.filter(s => { return (s.id != action.payload) } );
case FILTER_TASK :
filterStatus = action.payload;
return state.map(t => visibility(t, action.payload));
}
return state;
}
I read somewhere that modifying state is reducer is a bad practice, yet I feel that I'm doing it in my reducer.
Could anyone suggest the correct way of handling add,remove, update value state in the reducer ?
Thank you
i think you need two reducers: one for managing visibility stuff, one for adding, toggling and removing tasks.
so for the second part i would like do this.
export const ADD_TASK = 'ADD_TASK';
export const TOGGLE_TASK = 'TOGGLE_TASK';
export const REMOVE_TASK = 'REMOVE_TASK';
let todoId = 1;
export addTask = (text) => ({
type: ADD_TASK,
id: todoId++,
text
});
export toggleTask = (id) => ({
type: TOGGLE_TASK,
id
});
export removeTask = (id) => ({
type: REMOVE_TASK,
id
});
export function todosReducer(state = [], action) {
switch(action.type) {
case ADD_TASK :
return [...state, {id: action.id, text: action.text, completed: false}];
case TOGGLE_TASK :
return state.map(task => task.id !== action.id ? task : {...task, completed: !task.completed});
case REMOVE_TASK :
return state.filter(task => task.id !== action.id);
}
return state;
}

Resources