I was making a shopping cart but I ran into an error, the message exactly says: Warning: Cannot update a component (StateProvider
) while rendering a different component (CartDrinks
). To locate the bad setState() call inside CartDrinks
. I am using React Context to do it, but I can find a solution for that.
This is my Context:
import { createContext, useContext, useReducer } from "react";
export const StateContext = createContext();
export const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
)
export const useStateValue = () => useContext(StateContext);
and this is my Reducer:
export const initialState = {
cart: [],
}
const reducer = (state, action) => {
console.log(action)
switch (action.type) {
case "ADD_TO_CART":
return {
...state,
cart: [...state.cart, action.payload]
};
case "REMOVE_TO_CART":
let newCart = [...state.cart];
const index = state.cart.findIndex((cartItems) => cartItems.id === action.payload.id);
newCart.splice(index, 1);
if (index >= 0) {
newCart.splice(index, 1);
} else {
alert('Cant remove drink');
}
return { ...state, cart: newCart };
default:
return state;
}
}
export default reducer;
this is my component where i add product to cart:
import { useStateValue } from '../../context/Context';
import './card.css'
const Card = ({ id, name, image, price }) => {
const [{ }, dispatch] = useStateValue();
const addToCard = () => {
dispatch({
type: 'ADD_TO_CART',
item: {
id: id,
name: name,
image: image,
price: price,
}
})
}
return (
<div className="card">
<img src={image} alt={image} />
<div className="card__body">
<h3>{name}</h3>
<p>Price: $/.{price}</p>
</div>
<button onClick={addToCard}>Add to Cart</button>
</div>
)
}
export default Card
and this is my component where i remove the product from the cart
import { useStateValue } from "../../context/Context"
import './cart.css'
const CartDrinks = () => {
const [{ cart }, dispatch] = useStateValue();
const removeDrink = (id) => {
dispatch({
type: "REMOVE_TO_CART",
id: id
})
}
return (
<div className="cart__container">
{cart?.length === 0
?
<p>You don't have nothing</p>
:
cart.map((i) => (
<div className="cart" key={i.id}>
<img src={i.image} alt="" />
<div>
<h4>{i.name}</h4>
<p>$/.{i.price}</p>*
<button onClick={removeDrink(i.id)}>remove</button>
</div>
</div>
))
}
</div>
)
}
export default CartDrinks
Everything works fine until I try to enter the component where I delete the product, when entering that component I get the error.
The removeDrink
function is being immediately invoked when the CartDrinks
component mounts.
const removeDrink = (id) => {
dispatch({
type: "REMOVE_TO_CART",
id: id
})
}
...
<button onClick={removeDrink(i.id)}>remove</button> // <-- immediate call
Use an anonymous callback to call removeDrink
when clicked.
<button onClick={() => removeDrink(i.id)}>remove</button>
Or convert removeDrink
to a curried function to close over the id
and return the callback.
const removeDrink = (id) => () => {
dispatch({
type: "REMOVE_TO_CART",
id: id
})
}
...
<button onClick={removeDrink(i.id)}>remove</button>