DEMO - https://stackblitz.com/edit/react-68usks?devToolsHeight=33&file=src/App.js
How to properly change the state of an element?
I'm learning React in practice.
I want to change the state of the isInBasket property on click of the "Add to cart" button to make the button active.
Similar to how I did for the "Add to favorites" button.
Sample data:
{
id: 1
title: 'BMW',
isFavorite: false
isInBasket: false
}
I have a product card component and a cart component. The product card has two buttons "add to cart" and "Add to favorites".
I have two reducers, "basket" and "products". The basket reducer contains actions that are responsible for the state of the basket (add/remove/clear).
basketSlice.js
const basketSlice = createSlice({
name: 'basket',
initialState: {
products: [],
isEmpty: true,
},
reducers: {
setProductToBasket: (state, action) => {
const productOne = state?.products.find(
(p) => p.id === action.payload.id
);
if (!productOne) {
state.products.push({
...action.payload,
isInBasket: true,
});
state.isEmpty = false;
}
},
removeProductFromBasket: (state, action) => {
state.products = state?.products.filter((p) => p.id !== action.payload);
state.products.isInBasket = !state.products.isInBasket;
state.products.length > 0
? (state.isEmpty = false)
: (state.isEmpty = true);
},
clearBasket: (state) => {
state.products = [];
state.isEmpty = !state.isEmpty;
},
},
});
There is only one "add to favorites" action in the product reducer.
productsSlice.js
import { products } from '../data';
const productSlice = createSlice({
name: 'products',
initialState: {
products,
},
reducers: {
toggleFavoriteProduct: (state, action) => {
const productOne = state.products.find((p) => p.id === action.payload);
if (productOne) {
productOne.isFavorite = !productOne.isFavorite;
}
},
},
});
ProductItem.js
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setProductToBasket } from '../features/basketSlice';
import { toggleFavoriteProduct } from '../features/productsSlice';
export default function ProductItem({ product }) {
const dispatch = useDispatch();
// const { stateBasket, stateProducts } = useSelector((state) => state);
const onAddToBasket = (e) => {
e.stopPropagation();
dispatch(setProductToBasket(product));
};
const onToggleFavorite = (e) => {
e.stopPropagation();
dispatch(toggleFavoriteProduct(product.id));
};
return (
<div className="products-item">
<h4>{product.title}</h4>
<button
className={`btn ${product.isInBasket ? 'active' : ''}`}
onClick={onAddToBasket}
>
{product.isInBasket ? <></> : <></>}
</button>
<button
className={`btn ${product.isFavorite ? 'active' : ''}`}
onClick={onToggleFavorite}
>
{product.isFavorite ? <>❤️</> : <></>}
</button>
</div>
);
}
On the product card:
- on the "add to favorites" button, I will dispatch the
toggleFavoriteProductaction. - on the "add to cart" button, I will dispatch the
setProductToBasketaction.
And if you look in the reducers, you can see that the isInBasket flag changes to true only for objects in the basket state, and remains false in products.
I'm a little confused and trying to figure out how do I change the state of the isInBasket flag not in the basket state (stateBasket), but in the products state (stateProducts)?
Add a new action to the product reducer, checking if this product is in the cart or not?
If I understand the concept correctly, then you can't mix entities. By this I mean that you can not access another state or am I mistaken?
What is the best way to achieve the result?