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

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

但是像这样更新状态,

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

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


当前回答

我非常认真地对待关于创建组件状态的完整副本的问题。说到这里,我强烈推荐Immer。

import produce from 'immer';

<Input
  value={this.state.form.username}
  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />

这应该适用于React。PureComponent(即通过React进行浅状态比较),因为Immer巧妙地使用代理对象来有效地复制任意深度状态树。Immer也比Immutability Helper等库更加类型安全,是Javascript和Typescript用户的理想选择。


Typescript实用函数

function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
Draft<Readonly<S>>) => any) {
  comp.setState(produce(comp.state, s => { fn(s); }))
}

onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}

其他回答

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

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。

我用了这个方法。

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

   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" />

我知道这是一个老问题,但仍然想分享我是如何做到这一点的。假设构造函数中的状态是这样的:

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: {
        email: ""
      },
      organization: {
        name: ""
      }
    };

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

我的handleChange函数是这样的:

  handleChange(e) {
    const names = e.target.name.split(".");
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    this.setState((state) => {
      state[names[0]][names[1]] = value;
      return {[names[0]]: state[names[0]]};
    });
  }

并确保你的输入相应的名称:

<input
   type="text"
   name="user.email"
   onChange={this.handleChange}
   value={this.state.user.firstName}
   placeholder="Email Address"
/>

<input
   type="text"
   name="organization.name"
   onChange={this.handleChange}
   value={this.state.organization.name}
   placeholder="Organization Name"
/>

如果你在你的项目中使用formik,它有一些简单的方法来处理这些东西。下面是使用formik最简单的方法。

首先在formik initivalues属性或react中设置初始值。状态

这里,初始值是在react状态下定义的

   state = { 
     data: {
        fy: {
            active: "N"
        }
     }
   }

在formik initiValues属性中定义以上的initialValues字段

<Formik
 initialValues={this.state.data}
 onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
  <Form>
    <Field type="checkbox" name="fy.active" onChange={(e) => {
      const value = e.target.checked;
      if(value) setFieldValue('fy.active', 'Y')
      else setFieldValue('fy.active', 'N')
    }}/>
  </Form>
)}
</Formik>

制作一个控制台来检查状态更新为字符串而不是布尔值formik setFieldValue函数来设置状态或使用react调试器工具来查看formik状态值的变化。

创建一个状态的副本:

let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))

对该对象进行更改:

someProperty.flag = "false"

现在更新状态

this.setState({someProperty})