I'm creating a shopping cart app. When the user changes the quantity of an item, the price of the item should be updated along with it. However, in trying to implement this, I've encountered a bug where the items in the shopping cart are being duplicated rather than updated. Any help would be appreciated. Here is my code:
const [cart, setCart] = useState([]);
const handleQuantityChange = (e, product) => {
setCart((prevState) => [
...prevState,
...prevState.map((item) => {
if (item.id === product.id) {
return {
...item,
price: item.originalPrice * e.target.value,
quantity: e.target.value,
};
} else {
return item;
}
}),
]);
}
[...prevState, ...prevState.map()] is duplicating your list twice (one is the prevState, one is prevState.map())
You should modify your code like this
const [cart, setCart] = useState([]);
const handleQuantityChange = (e, product) => {
setCart((prevState) => [
...prevState.map((item) => {
if (item.id === product.id) {
return {
...item,
price: item.originalPrice * e.target.value,
quantity: e.target.value,
};
} else {
return item;
}
}),
]);
}
Another way without prevState but cart state
const [cart, setCart] = useState([]);
const handleQuantityChange = (e, product) => {
const updatedCart = cart.map((item) => {
if (item.id === product.id) {
return {
...item,
price: item.originalPrice * e.target.value,
quantity: e.target.value,
};
} else {
return item;
}
});
setCart(updatedCart);
}
[...prevState, ...prevState.map()] is duplicated one.
You can find the corresponding item in prevState by find function and update its price and quantity.
You can update setCart function call like the following.
setCart(prevState => {
const newState = prevState? [...prevState] : [];
const item = newState.find(x => x.id === product.id);
let qty = parseFloat(e.target.value);
qty = isNaN(qty) ? 0 : qty;
item.price = item.originalPrice * qty;
item.quantity = qty;
return newState;
});
Related
I use to react-paginate package for paginate. After searching, the pagination pattern is broken. While there should be 5 items per page, this situation breaks down after the search. I share my ss and codes. thanks for your time.
here is default paginate:
and after search paginate:
and here is my code:
const displayUsers = (users, setUsers, userCurrentPage) => { // pagination configs
const startIndex = (userCurrentPage - 1) * 5
const endIndex = userCurrentPage * 5
const productsToDisplay = users.slice(startIndex, endIndex)
setUsers(productsToDisplay)
}
const handleSearch = (e) => { // filter script
let searchValue = e.target.value
let filteredTasks = users.filter((task) => {
return task.UserID.toLowerCase().includes(searchValue.toLowerCase())
})
setPaginationUsers(filteredTasks)
}
useEffect(() => {
if (searchfield === '') {
setPaginationUsers(users)
} else {
const dynamicFilter = users.filter((user) => {
return user.UserID.toLowerCase().includes(searchfield.toLowerCase())
})
setPaginationUsers(dynamicFilter)
}
}, [searchfield])
// And here is mapping area
{paginationUsers.map((userDetail, index) => {
const {
UserID,
Country,
City,
MMA,
Time,
Game,
Revenue,
Timezone,
Device_Model,
Ad_Type,
SubNetwork,
} = userDetail
if (typeof userDetail.Revenue === 'string') {
userDetail.Revenue = parseFloat(userDetail.Revenue).toFixed(6)
}
return (
<tr key={index}>
<td>{UserID}</td>
<td>{Country}</td>
<td>{City}</td>
<td>{MMA}</td>
<td>{SubNetwork}</td>
<td>{Time}</td>
<td>{Revenue}</td>
<td>{Game}</td>
<td>{Timezone}</td>
<td>{Device_Model}</td>
<td>{Ad_Type}</td>
</tr>
)
})}
So this is my app which i have created in react and store data in firestore, i have a form in which ingredients is an array, i can dynamically add the input feilds and when i submit the form it gets submiited and the data gets stored in firebase. My problem is when i click the add feild button instead of one feild two feilds are simultaneously created and i am unable to understand how to do that so if anyone can explain what to do thanks .
code :
function App() {
const [recipes, setRecipes] = useState([])
const [form, setForm] = useState({
ingredients: [],
})
const [popupActive, setPopupActive] = useState(false)
const recipesCollectionRef = collection(db, "recipes")
useEffect(() => {
onSnapshot(recipesCollectionRef, snapshot => {
setRecipes(snapshot.docs.map(doc => {
return {
id: doc.id,
viewing: false,
...doc.data()
}
}))
})
}, [])
const handleView = id => {
const recipesClone = [...recipes]
recipesClone.forEach(recipe => {
if (recipe.id === id) {
recipe.viewing = !recipe.viewing
} else {
recipe.viewing = false
}
})
setRecipes(recipesClone)
}
const handleSubmit = e => {
e.preventDefault()
if (
!form.ingredients ||
) {
alert("Please fill out all fields")
return
}
addDoc(recipesCollectionRef, form)
setForm({
ingredients: [],
})
setPopupActive(false)
}
const handleIngredient = (e, i) => {
const ingredientsClone = [...form.ingredients]
ingredientsClone[i] = e.target.value
setForm({
...form,
ingredients: ingredientsClone
})
}
const handleIngredientCount = () => {
setForm({
...form,
ingredients: [...form.ingredients, ""]
})
{ recipe.viewing && <div>
<h4>Ingredients</h4>
<ul>
{ recipe.ingredients.map((ingredient, i) => (
<li key={i}>{ ingredient }</li>
))}
</ul>
As far as I have understood. Just do like below,
const handleIngredientCount = () => {
setForm({
...form,
ingredients: [...form.ingredients, "", ""],
})
}
You will be created with two input fields simultaneously instead of one when you click the add ingredient button.
I'm new to React and I created a small admin panel where you can add, edit, remove products. I would like to display 3 products from API when someone opens the app the first time and don't have edited products yet, but this data only shows if I manually refresh the page. I only want to display that if edited product is false, but initially I set edited products to false yet somehow it's not displaying, though I see the data as well as edited is set to false in the console.
Demo
https://react-storeadminpanel.herokuapp.com/
Here is the related code:
const Products = () => {
const {products, setProducts, setAllProducts, allProducts, editedItems, setEditedItems} = useProduct();
useEffect(() => {
async function fetchProducts() {
const res = await axios.get('https://a.nacapi.com/LimeGreen/products/').catch(err => console.log(err));
if(res) {
setProducts(res.data)
setEditedItems(false);
if(allProducts.length === 0 && editedItems === false) setAllProducts(products);
if(allProducts.length === 0 && editedItems === true) setAllProducts(allProducts);
if(allProducts.length > 0) setAllProducts([...allProducts]);
}
return res;
}
fetchProducts();
}, []);
return (
<Wrapper classname="wrapper">
<h1>All Products</h1>
<Cards>
{!!allProducts.length && (
allProducts.map(product => (
<ProductCard name={product.name} description={product.Description} price={product.Price} discount={product.Discount} key={product.uuid}/>
))
)}
</Cards>
</Wrapper>
)
}
The context, where I use LocalStorage
export const ProductContext = React.createContext();
export function useProduct() {
return useContext(ProductContext);
}
export function ProductProvider({children}) {
const [products, setProducts] = useLocalStorage('Api Data', []);
const [addedProduct, setAddedProduct] = useLocalStorage('Added Item', []);
const [allProducts, setAllProducts] = useLocalStorage('All Products', []);
const [editedItems, setEditedItems ] = useLocalStorage('Edited', false);
const [isAdded, setIsAdded] = useState(false);
const value = {
products,
setProducts,
addedProduct,
setAddedProduct,
allProducts,
setAllProducts,
editedItems,
setEditedItems,
isAdded,
setIsAdded,
}
return (
<ProductContext.Provider value={value}>
{children}
</ProductContext.Provider>
)
}
And Code where I set edit products to true
const ProductEdit = () => {
const {allProducts, setAllProducts, setEditedItems} = useProduct();
const [editProductId, setEditProductId] = useState(null);
const [editForm, setEditForm] = useState({
name: "",
Description: "",
Price: "",
Discount: "",
})
const saveEditHandler = (e) => {
e.preventDefault();
const fieldName = e.target.getAttribute("name");
const fieldValue = e.target.value;
const newForm = {...editForm};
newForm[fieldName] = fieldValue;
setEditForm(newForm);
}
const editHandler = (e, product) => {
e.preventDefault();
setEditProductId(product.uuid);
const formValues = {
name: product.Name,
Description: product.Description,
Price: product.Price,
Discount: product.Discount
}
setEditForm(formValues);
}
const submitEditsHandler = (e) => {
e.preventDefault();
const editedProduct = {
name: editForm.Name,
Description: editForm.Description,
Price: editForm.Price,
Discount: editForm.Discount,
uuid: editProductId
}
const newProducts = [...allProducts];
const index = allProducts.findIndex((product) => product.uuid === editProductId);
newProducts[index] = editedProduct;
setAllProducts(newProducts);
setEditedItems(true);
setEditProductId(null);
}
const cancelHandler = () => {
setEditProductId(null);
}
const deleteHandler = (productId) => {
const newProducts = [...allProducts];
const index = allProducts.findIndex((product) => product.uuid === productId);
newProducts.splice(index, 1);
setAllProducts(newProducts);
setEditedItems(true);
};
I manage to delete the tasks (you can see it in the console.log) but I don't know how to render the result. I really appreciate your help. Link CodeSanbox: https://codesandbox.io/s/trello-task-forked-2xsh8?file=/src/App.js
const addItem = (e) => {
e.preventDefault();
const item = { id: uuidv4(), content: text };
const requestedColumnId = Object.entries(columns).find(
(i) => i[1].name === "Requested"
)[0];
const column = columns[requestedColumnId];
setColumns({
...columns,
[requestedColumnId]: {
...column,
items: [...column.items, item]
}
});
setText("");
};
const deleteItem = id => {
const requestedColumnId = Object.entries(columns).find(
(i) => i[1].name === "Requested"
)[0];
const column = columns[requestedColumnId];
const arrFiltered = column.items.filter(item => item.id !== id)
console.log('arrFiltered', arrFiltered)
setColumns({
...columns,
[requestedColumnId]: {
...column,
items: [...column.items]
}
});
}
Here is a straight forward solution. You did it everything correctly but you missed something. Just update your delete function to the following
const deleteItem = (id) => {
const requestedColumnId = Object.entries(columns).find(
(i) => i[1].name === "Requested"
)[0];
const column = columns[requestedColumnId];
setColumns({
...columns,
[requestedColumnId]: {
...column,
items: [...column.items.filter((item) => item.id !== id)]
}
});
};
Your mistake is that you're filtering the array upon deletion. But you're not updating the main item. So I solved it by adding the array filter in to the main item and removing your filter. Just like this.
setColumns({
...columns,
[requestedColumnId]: {
...column,
items: [...column.items.filter((item) => item.id !== id)]
}
});
I can see my array in state, but I don't know why elements of array doesn't display on the app interface.
const [members, setMembers] = useState([])
useEffect( () => {
getMembers();
}, [props.event])
const getMembers = () => {
let new_members = [];
console.log(props.event)
props.event && props.event.uczestnicy.map(member => {
member.get().then(doc => {
let new_member;
new_member = {
...doc.data(),
id: doc.id
}
new_members.push(new_member)
})
setMembers(new_members)
})
console.log(new_members)
console.log(members)
}
[...]
{members && members.map(member => {
console.log('mem',member)
return(
<div key={member.id}>
{member.nick}
</div>
)
})}
So I can see this array in Components using React Developer Tools, but even console.log doesn't see it in the moment of performing.
And console.log(new_members) and console.log(members) result :
Your member values are fetch asynchronously, so its ideal if you set state only after all the values are resolved. For this you can use a Promise.all
const getMembers = async () => {
let new_members = [];
console.log(props.event)
if(props.event) {
const val = await Promise.all(props.event.uczestnicy.map(member => {
return member.get().then(doc => {
let new_member;
new_member = {
...doc.data(),
id: doc.id
}
return new_member
})
});
setMembers(values);
console.log(values);
}
}