Reputation: 35
I am fetching data and I have mapped the data to display it. Everything is fine but when I try to update the fetched data (which I have already stored in an array, just want to change it through other way , mentioned below) does not update.I have given the data below,I just want to change the 'quantity' attribute of each object respectively when I click the button but everytime I try to use the spread operator and give a comma and try to define the attribute of the mapped objects which is 'quantity' here, it doesnot update the quantity attribute and replaces the whole array.Can you please tell me how to write a onClick function which copies the whole array(using spread operator) and then update the quantity attribute.(the fetched data is stored in an array)
0: {id: 11, ordered: true, quantity: 5, user: 1, item: {…}}
1: {id: 12, ordered: true, quantity: 1, user: 1, item: {…}}
2: {id: 14, ordered: true, quantity: 1, user: 1, item: {…}}
3: {id: 15, ordered: true, quantity: 4, user: 1, item: {…}}
4: {id: 17, ordered: true, quantity: 3, user: 1, item: {…}}
Here is the data I am fetching.(above)
0:
id: 11
item: {id: 10, title: "Watch", image: "https://github.com/divanov11/django_ecommerce_mod5/blob/master/static/images/watch.jpg?raw=true", price: "$20"}
ordered: true
quantity: 5
user: 1
It is the data in each object(above).I want to keep everything same and just change the quantity attribute of each object respectively.Below is my code.
import React, { useEffect, useState } from 'react';
let Cart = () => {
let [myItems, setMyItems] = useState([])
let fetchData = () => {
fetch('http://127.0.0.1:8000/api/get-order-item/' + sessionStorage.getItem('id'), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer' + ' ' + sessionStorage.getItem('token')
}
}).then(
(response) => {
return response.json()
}
).then(
(data) => {
if (data) {
setMyItems(data)
}
})
}
useEffect(
() => {
fetchData()
console.log(myItems)
}, []
)
return (
<div>
{
myItems.map(
(value) => {
return (
<div className='row w-100 m-auto' >
<div className="card-deck container-fluid m-auto bg-light">
<div className="card bg-light mt-4 mb-2 " >
<img src={value.item.image} className="card-img-top" alt="..." />
<div className="card-body border-top ">
<div className='d-flex justify-content-between' >
<h4>{value.item.title}</h4>
<div className='d-flex flex-row-reverse' >
<svg onClick={ () => { setMyItems( (prevState) => { return [...prevState , {value : { quantity : prevState.quantity - 1 }} ] } ) } } id='add' xmlns="http://www.w3.org/2000/svg" width="28" height="23" fill="currentColor" class="bi bi-plus" viewBox="0 0 16 16" >
<path fill-rule="evenodd" d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
</svg>
<input defaultValue={value.quantity} className='w-25 h-75 text-center' />
<svg onClick={() => { console.log(myItems) }} xmlns="http://www.w3.org/2000/svg" width="32" height="25" fill="currentColor" class="bi bi-dash" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z" />
</svg>
</div>
</div>
<h5>{value.item.price}</h5>
<div class="form-check d-flex justify-content-between">
<input type="checkbox" class="form-check-input" id="quantity" checked={value.ordered} />
<label class="form-check-label" for="exampleCheck1">Proceed this item to checkout</label>
<button type="button" class="btn btn-success ">Save changes</button>
</div>
</div>
</div>
</div>
</div >
)
}
)
}
</div >
)
}
export default Cart;
Inside the map function , where I am mapping the objects , I just want to create a button which changes the quantity attribute of their respectively objects
Upvotes: 4
Views: 1205
Reputation: 57344
The attempt is:
setMyItems( (prevState) => { return [...prevState , {value : { quantity : prevState.quantity - 1 }} ] } ) } }
Firstly, this is pretty hard to read inline, so I'd move this to a function so you can understand what you're working with.
Next, instead of ...prevState
, which spreads the entire previous array and appends a new tail with {value : ...}
, find the item you want to mutate by index or id (sort of an application-specific decision--probably id is better) and slice the array into three pieces:
i
i
i + 1
to the endThis can be done with slicing and spreading to build a new array:
const newArr = [...arr.slice(0, i), arr[i], ...arr.slice(i + 1)];
Last step: manipulating the arr[i]
object to adjust the quantity. It looks like quantity is on the top-level, so:
{...arr[i], quantity: newQuantity}
Putting it all together, here's a minimal, runnable example you can apply to your code:
<script type="text/babel" defer>
const {useEffect, useState} = React;
const mockItems = [
{id: 11, quantity: 5, item: {title: "foo"}},
{id: 12, quantity: 1, item: {title: "bar"}},
{id: 14, quantity: 1, item: {title: "baz"}},
{id: 15, quantity: 4, item: {title: "quux"}},
{id: 17, quantity: 3, item: {title: "corge"}},
];
fetch = () => new Promise((resolve, reject) =>
setTimeout(() =>
resolve({json: async () => mockItems})
, 1000)
);
const Cart = () => {
const [items, setItems] = useState([]);
useEffect(() => {
(async () => {
const response = await fetch("some endpoint");
setItems(await response.json());
})();
}, []);
const setQuantity = (id, quantity) => {
const i = items.findIndex(e => e.id === id);
setItems(prev => [
...prev.slice(0, i),
{...prev[i], quantity},
...prev.slice(i + 1),
]);
};
return (
<ul>
{!items.length ? "loading..." :
items.map(e =>
<li key={e.id}>
<div>{e.item.title}</div>
<div>
Qty: {e.quantity}
<button
onClick={() =>
setQuantity(e.id, e.quantity + 1)
}
>+</button>
<button
onClick={() =>
setQuantity(e.id, e.quantity - 1)
}
>-</button>
</div>
</li>
)
}
</ul>
);
};
ReactDOM.createRoot(document.querySelector("#app"))
.render(<Cart />);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
Upvotes: 2