sg.cc
sg.cc

Reputation: 1816

How do I chain state updates?

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 setStates using the same cloned fields object for each update. The object is not updated between setStates, 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 setStates, 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

Answers (1)

Yury Tarabanko
Yury Tarabanko

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

Related Questions