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

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

但是像这样更新状态,

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

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


当前回答

为了为嵌套对象设置setState,你可以遵循下面的方法,因为我认为setState不处理嵌套更新。

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

其思想是创建一个虚拟对象,对其执行操作,然后用更新的对象替换组件的状态

现在,展开操作符只创建对象的一层嵌套副本。如果你的状态是高度嵌套的,比如:

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

你可以在每一层使用展开运算符setState

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

然而,随着状态嵌套越来越多,上面的语法会变得越来越糟糕,因此我建议你使用不可变性帮助包来更新状态。

关于如何使用immutability-helper更新状态,请参阅此回答。

其他回答

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

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

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

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

我用了这个方法。

如果你有一个这样的嵌套状态:

   this.state = {
          formInputs:{
            friendName:{
              value:'',
              isValid:false,
              errorMsg:''
            },
            friendEmail:{
              value:'',
              isValid:false,
              errorMsg:''
            }
}

你可以声明handleChange函数来复制当前状态并重新赋值

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let statusCopy = Object.assign({}, this.state);
    statusCopy.formInputs[inputName].value = inputValue;

    this.setState(statusCopy);
  }

这里是带有事件侦听器的HTML

<input type="text" onChange={this.handleChange} " name="friendName" />

为了为嵌套对象设置setState,你可以遵循下面的方法,因为我认为setState不处理嵌套更新。

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

其思想是创建一个虚拟对象,对其执行操作,然后用更新的对象替换组件的状态

现在,展开操作符只创建对象的一层嵌套副本。如果你的状态是高度嵌套的,比如:

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

你可以在每一层使用展开运算符setState

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

然而,随着状态嵌套越来越多,上面的语法会变得越来越糟糕,因此我建议你使用不可变性帮助包来更新状态。

关于如何使用immutability-helper更新状态,请参阅此回答。

对于在2022年读书的人来说:

    constructor(props) {
        super(props);
        this.state = {
            someProperty: {
                flag: true
            }
            otherValues: '',
            errors: {}
        };

        this.handleInputChange = this.handleInputChange.bind(this);
    }

    handleInputChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
        const someProperty = { ...this.state.someProperty };
        someProperty[name] = value;

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

下面是一个完整的例子,使用嵌套状态(一层)和这个答案中的解决方案,对于一个实现为类的组件:

class CaveEditModal extends React.Component {

  // ...

  constructor(props, context) {
    super(props);
    this.state = {

      tabValue: '1',
      isModalOpen: this.props.isModalOpen,

      // ...
      caveData: {
        latitude: 1,
        longitude: 2      
      }
    };

    // ... 

    const updateNestedFieldEvent = fullKey => ev => { 
      
      var [parentProperty, _key] = fullKey.split(".", 2);

      this.setState({[parentProperty]: { ...this.state[parentProperty], [_key]: ev.target.value} });
    };
    // ...

    this.handleLatitudeChange = updateNestedFieldEvent('caveData.latitude');
    this.handleLongitudeChange = updateNestedFieldEvent('caveData.longitude');
  }

  render () {    
    return (
      <div>
         <TextField id="latitude" label="Latitude" type="number" value={this.state.caveData.latitude} onChange={this.handleLatitudeChange} />
         <TextField id="longitude" label="Longitude" type="number" value={this.state.caveData.longitude} onChange={this.handleLongitudeChange} />
         <span>lat={this.state.caveData.latitude} long={this.state.caveData.longitude}</span>
      </div>
    );
  };

}

注意,状态更新器函数updateNestedFieldEvent只适用于一层嵌套对象,比如a.b,而不是a.b.c。