score:11

Accepted answer

I can think of a few other ways to achieve it.

Deconstructing every nested element and only overriding the right one :

this.setState(prevState => ({
    inputs: {
        ...prevState.inputs,
        username: {
            ...prevState.inputs.username,
            touched: true
        }
    }
}))

Using the deconstructing operator to copy your inputs :

this.setState(prevState => {
    const inputs = {...prevState.inputs};
    inputs.username.touched = true;
    return { inputs }
})

EDIT

First solution using computed properties :

this.setState(prevState => ({
    inputs: {
        ...prevState.inputs,
        [field]: {
            ...prevState.inputs.[field],
            [action]: value
        }
    }
}))

score:0

Use the spread operator

  let {foo} = this.state;

  foo = {
    ...foo,
    bar: baz
  }

  this.setState({
    foo
  })

score:1

You can try with nested Object.Assign:

 const newState = Object.assign({}, state, {
  inputs: Object.assign({}, state.inputs, {
    username: Object.assign({}, state.inputs.username, { touched: true }),
  }),
});

};

You can also use spread operator:

{
  ...state,
  inputs: {
    ...state.inputs,
      username: {
      ...state.inputs.username,
      touched: true
   }
}

This is proper way to update nested property and keep state immutable.

score:1

I made a util function that updates nested states with dynamic keys.

 function _recUpdateState(state, selector, newval) {
          if (selector.length > 1) {
            let field = selector.shift();
            let subObject = {};
            try {
              //Select the subobject if it exists
              subObject = { ..._recUpdateState(state[field], selector, newval) };
            } catch {
              //Create the subobject if it doesn't exist
              subObject = {
                ..._recUpdateState(state, selector, newval)
              };
            }
            return { ...state, [field]: subObject };
          } else {
            let updatedState = {};
            updatedState[selector.shift()] = newval;
            return { ...state, ...updatedState };
          }
        }
        
        function updateState(state, selector, newval, autoAssign = true) {
          let newState = _recUpdateState(state, selector, newval);
          if (autoAssign) return Object.assign(state, newState);
          return newState;
        }
        
// Example

let initState = {
       sub1: {
          val1: "val1",
          val2: "val2",
          sub2: {
             other: "other value",
             testVal: null
          }
       }
    } 
    
    console.log(initState)
    updateState(initState, ["sub1", "sub2", "testVal"], "UPDATED_VALUE")
    console.log(initState)

You pass a state along with a list of key selectors and the new value.

You can also set the autoAssign value to false to return an object that is a copy of the old state but with the new updated field - otherwise autoAssign = true with update the previous state.

Lastly, if the sequence of selectors don't appear in the object, an object and all nested objects with those keys will be created.


Related Query

More Query from same tag