-2

Cannot figure out why this state will not update, there are other values other than personalValues that is why it is nested.

The state

this.state = {
   personalValues: [
     {targetLevel: 0},
     {firstCommission: 0.36},
     {cancels: 0.10},
     {averagePolicy: 1150},
     {productionWeeks: 48},
     {presPerWeek: 15},
     {closingRate: 0.40},
     {appsPerWeek: 6}
   ]

The handler I tried

handleFormChange(event){
  this.setState({[event.target.name]: event.target.value})
}

The Component

const personalFormValues =
    { 
      class: ["firstCommission", "cancels", "averagePolicy",
              "productionWeeks", "presPerWeek", "closingRate",
              "appsPerWeek"],
    };
var className = personalFormValues.class[i];   
    <TextField
                className={className}
                type="number"
                placeholder={personalFormValues.placeholder[i]}
                onChange={this.handleFormChange}
                onBlur={this.changeValues}
                name={className}
                fullWidth={true}
                pattern="[0-9]*"
                required />
craigsale29
  • 35
  • 1
  • 8

4 Answers4

3

In terms of the state there is only a single value this.state.personalValues. To React it doesn't matter if that value is an array of objects. Instead simply create a variable that stores the current value let obj = this.state.personalValues. Then iterate through obj until you find a match on the key with event.target.name and set the value of that obj to event.target.value. Or more simply doesn't use an array and use an object directly:

this.state = {
   personalValues: {
     targetLevel: 0,
     firstCommission: 0.36,
     cancels: 0.10,
     averagePolicy: 1150,
     productionWeeks: 48,
     presPerWeek: 15,
     closingRate: 0.40,
     appsPerWeek: 6
   }

Then you could just do:

let obj = this.state.personalValues;
obj[event.target.name] = event.target.value;
this.setState({personalValues: obj});
Spencer Wieczorek
  • 21,229
  • 7
  • 44
  • 54
  • This creates a new state rather than updating the already existing state – craigsale29 Sep 24 '18 at 18:20
  • 1
    @craigsale29 that's the goal. It is a very good practice in react to not mutate state. You simply dump the old state and make a new state. It is much more performant and less error-prone. In fact, you could do better than this example by using Object.assign to set the new values. – Adam LeBlanc Sep 24 '18 at 18:21
  • @craigsale29 The state `this.state.personalValues` is the only state that exist to React, as it does not support nested states. React just sees it as `this.state{ personalValues: }`. That is why you need to get the entire object and change what's needed and set the state `personalValues`. Otherwise you could drop `personalValues` and make each child object it's own state. – Spencer Wieczorek Sep 24 '18 at 18:34
  • Thank you both for the helpful comments, this now works – craigsale29 Sep 24 '18 at 18:54
1

React checks elements of this.state for shallow pointer equivalency before triggering a re-render. Therefore, if you in-place-modify a recursive data-structure, then React will fail to notice a change, because shallow comparison between previous and next state object shows no difference.

Thus you cannot write to a recursive structure, nor do:

this.setState(prevState => ({personalValues: Object.assign(prevState.personalValues, nextValues)}));

because Object.assign performs in-place modification.

You must do either of:

this.setState(prevState => ({personalValues: Object.assign({}, prevState.personalValues, nextValues)}));
this.setState(prevState => ({personalValues: {...prevState.personalValues, ...nextValues}}));

This will create a new object and copy the data-structure in it. Be aware that only the first level object is created anew. Recursive copying gets ugly; see https://stackoverflow.com/a/43041334/1235724

Nonetheless, if you have a very large data structure stored in your state, you may want to use in-place modification together with this.forceUpdate(), eschewing copying altogether.

Jens Jensen
  • 1,038
  • 1
  • 10
  • 20
0

Do you need personalValues to be an array? If you specify it like this, that should work:

state = {
  personalData: {
    targetLevel: 0,
    firstCommission: 0.36
    ....
  }
}

handleFormChange({target}){
  this.setState(prevState => ({
    personalValues: {
      ...prevState.personalValues,
      [target.name]: target.value
    }
  })
}
Darek Gala
  • 157
  • 1
  • 2
  • 7
0

Better way to copy old state to a new variable then change the values if you want and then update the state value with that newly created type variable.

let personalValuesCopy  = this.state.personalValues;

personalValues.dynamicKey = updated values   // Update values may be using onChange events or some custom data

this.setState({ personalValues : personalValuesCopy  }) // Update state with new object
Alok Rai
  • 75
  • 9