This question already has answers here:
React.js: setState overwriting, not merging
(3 answers)
Closed 2 years ago.
I am trying to have items be addable (done) however I am having an issue with trying to remove an item from the users cart.
Removefromcart = (cart, id) => {
// using an updater function
this.setState(({
// keep everything except the item with the id that was passed in
cart: this.state.cart.filter(cart => cart.id !== id),
// item price - current state total
// total: state.total - price
}));
};
As you can see I am trying to use a filter to remove everything except the id of what was clicked.
However right now it seems to be erasing the array completely. Before that it wasn't affecting the array at all.
In case it is relevant here is a link to my full code: https://codesandbox.io/s/vigilant-dubinsky-0d57h
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
docs
To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:
Blockquote
In your case, you'll need:
this.setState(prevState => ({
cart: prevState.cart.filter(cart => cart.id !== id),
});
Related
This question already has answers here:
Firestore Update single item in an array field
(2 answers)
How can I update an object inside of an array in firestore?
(1 answer)
Closed 6 months ago.
I'm trying to make a button that can edit an item within a documents array in firebase. Basically, the functionality I'm building is: you click an EDIT button, and it changes a tag into an tag where you would make your edit/update to the array item. Then you would click a DONE button and the update to the array would happen. For some reason, I can only find documentation on firebase for updating a document, and not an item inside the array. I've tried using arrayUnion but it only adds a new item to the array. I'm trying to update the info[0].tags in the screenshot below. Please let me know if I'm missing information to help make this work.
My issue seems to be I can't do: info[i]: arrayUnion({tags: tags}) so I'm not sure how to tell the updateDoc function which item in the info array I need to update.
Here's the code:
const editCard = async (tags, i) => {
const fieldRef = doc(db, "labels", activeLabels);
await updateDoc(fieldRef, {
info[i]: arrayUnion({ tags: tags }),
});
console.log(tags);
getLabels();
findLabel(activeLabels);
toast.success("Tags Updated!");
};
[![firebase document and array][1]][1]
[1]: https://i.stack.imgur.com/PDJcc.png
When clicking on a button i call a function to set a unique ID of an object and then push the object inside an array:
const addItems = (item) => {
item.uniqueId = addedItems.length + 1;
addedItems.push(item);
};
It works as intended if i add the items slowly, but if i press multiple times a second on the button the unique id will be the same (and a wrong one, not the one in line: for example if i have 2 items in the array with unique id of 1 and 2 and then press fast on the button 3 times, those 3 objects will have a unique id of 5, all 3 of them). What is wrong here?
Assuming it is state you must always update it in immutable way:
setAddedItems(ps=>[...ps,{...item,uniqueId:ps.length+1}])
And indeed this way as mentioned in other answer if you remove items, then add new ones, the ids may repeat; but if items aren't removed, then this approach is ok.
You can also check uuid if you want to remove items too.
Your code have many minuses, I recommend you use nanoid.
If you will remove item from your useState, some ids could be same. Better use an index for this if you would not change your state (remove items)
It will look like:
const addItems = (item) => {
setAddedItems((addedItems) => [
...addedItems, { ...item, uniqueId: nanoid() }
])
}
I've seen typescript solution with universal handle change function, but in my case it works bad. Need help to understand why this happens.
When I add new product - it adds correctly, but when I try to clear Input manually - first rerender it also changes already added content.
Algorithm to see this mistake: fill inputs, add submit and remove one letter from title input - you'll see that it affects already added title in another state, but only on first rerender
Sandbox: https://codesandbox.io/s/restless-dream-do7bqh?file=/src/App.tsx
The below code-sample may be a solution to achieve the desired objective:
In the handleChange method:
setProduct(prev => ({ ...prev, [name]: value }));
Explanation
The prev value of product is taken as-is
Then, using the ... spread-operator all of prev's props are spread
This is now followed with [name] which is a new prop being added with value as the corresponding value
Any existing prop will be replaced.
Suppose name variable has the value 'title' and value is 'Testing 123', then the product object becomes:
product = {
id: previousValueOfId,
price: previousValueOfPrice,
title: 'Testing 123'
}
Here, previousValueOfId and previousValueOfPrice are supposed to be the values.
Why OP's code did not get the desired result
The code is:
setProduct((prev) => {
(prev as any)[name] = value;
const newValue = { ...prev };
return newValue;
});
This takes the prev product, and changes it's [name] prop.
When prev is changed, it may impact UI elements which are rendered using product.
(NOTE: I am still very new to ReactJS & if this is incorrect kindly update appropriately).
I'm trying to render a modal on the click of a button. I'm rendering the Modal perfectly fine. And I have two dropdowns in the modal which can get duplicated when we click on the add more button in the modal. When I click on the add more button the dropdowns get duplicated.
The Problem here is:
1. When ever we choose the first option in the first dropdown and go on to select the second option in the second dropdown the second dropdown is not showing any value for us. And the value of the first dropdown is getting updated with it's second value.
2. When we add the other set of dropdowns the problem 1 is there and with that above problem the values of the first dropdown are getting in sync with all of the first dropdowns.
You can have a look at the problem in the CodeSandbox that I've attached below:
https://codesandbox.io/s/winter-wildflower-oz7do?file=/src/App.js
When ever we choose the first option in the first dropdown and go on to select the second option in the second dropdown the second dropdown is not showing any value for us. And the value of the first dropdown is getting updated with it's second value.
This is what you telling the code to do with calling the function
const handleChange = (event) => {
setReview(event.target.value);
};
In the moment you are changing the option, the review value gets updated; thus, the value in your first dropdown is changing, not the option value from your second dropdown.
When we add the other set of dropdowns the problem 1 is there and with that above problem the values of the first dropdown are getting in sync with all of the first dropdowns.
This is, what you tell the system to do, too. Because you are just updating a string state that you declared with const [review, setReview] = useState("");
As you noticed, the problem is two-faced here.
The approach would be that you 1) ensure you are updating the right value (review or option) and 2) that you save the updated values within the arrays you already created and that you populate with another object once you click on the "+ add more" button, meaning those to arrays:
const [reviewInput, setReviewInput] = useState([{ review: "" }]);
const [reviewOption, setReviewOption] = useState([{ option: "" }]);
What means, in order to update the review input array, you can do the following:
const handleChangeReviewInput = (event, index) => {
setReviewInput([
...reviewInput.map((item, key) =>
key === index ? { review: event.target.value } : item
)
]);
};
The function is checking for the position in the array you try to change. If found: update the input with the given value.
In your dropdown rendering:
<Select
labelId="demo-simple-select-standard-label"
id="demo-simple-select-standard"
value={reviewInput.review} // taking the review string of your item in the array
onChange={(e) =>
handleChangeReviewInput(e, index) // passing the index (position in the array)
}
label="Review"
>...</Select>
And similar you can do with your review options:
const handleChangeOption = (event, index) => {
setReviewOption([
...reviewOption.map((item, key) =>
key === index ? { option: event.target.value } : item
)
]);
};
and
<Select
labelId="demo-simple-select-standard-label"
id="demo-simple-select-standard"
value={item.option} // taking option string from your item in the array
onChange={(e) => handleChangeOption(e, index)} // passing index
label="Option"
>...</Select>
See the working version:
https://codesandbox.io/s/jovial-greider-9uthk?file=/src/App.js
In general you might want to consider to have one array for both, your review input and your review option; and then have one function to update either the review input or the option.
I have a list has many objects in it, like this:
[{id: 3, kategori: 'Kultur', name: 'Topkapı Sarayı',},{id: 5, kategori: 'Diger',name: 'Topkapı Sarayı', description: "xxxx"},]
and I want to return objects of this list one by one on a card by (prev, next) buttons. so I created a counter and defined the increment (for next button) and reduce(for prev button) functions. Then I used this counter as index of the list. Finally I mapped this list like that:
this.viwedList = [ myList()[this.state.counter] ].map((tur) => (
<CreatContent
turName={tur.name}
img={tur.img}
country={tur.country}
increment={this.increment}
reduce={this.reduce}
/>
));
this.setState(() => {
return {
mainContent: this.viwedList
};
});
When I click on the prev and next buttons, the counter value increases and decreases. The main problem is that the displayed object does not change instantly, when I click on another category (list) and then return to the same card, then it changes.
[first img of issua][1] [second img of issua][2] [last img of
issua][3]
[1]: https://i.stack.imgur.com/mjzRf.jpg [2]:
https://i.stack.imgur.com/7tCVA.jpg [3]:
https://i.stack.imgur.com/qqS2E.jpg
When you render objects from an array, they need to have a unique key property. This lets React keep track of them, so it can know what things need to be updated on a new render.
Without a key, React may fail to update the components, as it fails to recognize that they have changed, which is the behavior you're seeing.
You can read more in the docs.