Reputation: 1816
I have a form with multiple controls that saves everything to a variable. Each control has an onChanged
function, which runs a state update with that control's new value:
function onChangedValUpdate(newVal){
let fields = clone(this.state.fields);
fields[controlId] = newVal;
this.setState({fields});
}
My controls are dynamically created, and when they are, they run onChangedValUpdate
on their initial value, if one is present. The problem is, sometimes a lot of controls are created at once, and React queues up its setState
s using the same cloned fields
object for each update. The object is not updated between setState
s, presumably for similar reasons to this question. This means that, effectively, all but one control's updates are overwritten.
I tried writing an over-smart routine which used setState
's callback to only run it if there isn't one already in progress and remember changes made to the fields
variable in between setState
s, but React went and ran all my queued updates simultaneously. Regardless, the routine felt too contrived to be right.
I'm sure this is a trivial and solved problem, but I can't seem to formulate my question in a Googleable way. How do I chain state updates that happen concurrently, and of which there may be any number?
EDIT For posterity, my solution, thanks to Yuri:
function onChangedValUpdate(newVal){
this.setState( state => {
let fields = clone(state.fields);
fields[controlId] = newVal;
return {fields};
}
}
Upvotes: 4
Views: 1885
Reputation: 45121
You could pass a mutation function to setState
. This will prevent overwritting on batched updates because every callback will get the most recent previous state.
function onChangedValUpdate(newVal){
this.setState(function(state){
const fields = clone(state.fields)
fields[controlId] = newVal
return {fields: fields}
});
}
Or using object spread and enhanced object literals.
function onChangedValUpdate(newVal){
this.setState(({fields}) => ({fields: {...fields, [controlId]: newVal}}));
}
Upvotes: 3