我试图通过使用这样的嵌套属性来组织我的状态:

this.state = {
   someProperty: {
      flag:true
   }
}

但是像这样更新状态,

this.setState({ someProperty.flag: false });

是行不通的。怎样才能正确地做到这一点呢?


当前回答

如果你正在使用ES2015,你可以访问Object.assign。您可以使用它来更新嵌套对象。

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

您将更新的属性与现有属性合并,并使用返回的对象更新状态。

编辑:将一个空对象作为目标添加到赋值函数中,以确保状态不会像carkod指出的那样直接发生突变。

其他回答

如果你正在使用ES2015,你可以访问Object.assign。您可以使用它来更新嵌套对象。

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

您将更新的属性与现有属性合并,并使用返回的对象更新状态。

编辑:将一个空对象作为目标添加到赋值函数中,以确保状态不会像carkod指出的那样直接发生突变。

你应该把new state传递给setState。 新状态的引用必须与旧状态的引用不同。

所以试试这个:

this.setState({
    ...this.state,
    someProperty: {...this.state.someProperty, flag: true},
})

尽管嵌套并不是处理组件状态的真正方式,但有时对于单层嵌套来说很容易。

对于这样一个州来说

state = {
 contact: {
  phone: '888-888-8888',
  email: 'test@test.com'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

我使用的一个可重用的方法是这样的。

handleChange = (obj) => e => {
  let x = this.state[obj];
  x[e.target.name] = e.target.value;
  this.setState({ [obj]: x });
};

然后为你想要处理的每个嵌套传递obj名称…

<TextField
 name="street"
 onChange={handleChange('address')}
 />

这样就足够了,

const isObject = (thing) => {
    if(thing && 
        typeof thing === 'object' &&
        typeof thing !== null
        && !(Array.isArray(thing))
    ){
        return true;
    }
    return false;
}

/*
  Call with an array containing the path to the property you want to access
  And the current component/redux state.

  For example if we want to update `hello` within the following obj
  const obj = {
     somePrimitive:false,
     someNestedObj:{
        hello:1
     }
  }

  we would do :
  //clone the object
  const cloned = clone(['someNestedObj','hello'],obj)
  //Set the new value
  cloned.someNestedObj.hello = 5;

*/
const clone = (arr, state) => {
    let clonedObj = {...state}
    const originalObj = clonedObj;
    arr.forEach(property => {
        if(!(property in clonedObj)){
            throw new Error('State missing property')
        }

        if(isObject(clonedObj[property])){
            clonedObj[property] = {...originalObj[property]};
            clonedObj = clonedObj[property];
        }
    })
    return originalObj;
}

const nestedObj = {
    someProperty:true,
    someNestedObj:{
        someOtherProperty:true
    }
}

const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty) 
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);