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

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

但是像这样更新状态,

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

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


当前回答

创建一个状态的副本:

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

对该对象进行更改:

someProperty.flag = "false"

现在更新状态

this.setState({someProperty})

其他回答

根据框架的标准,不确定这在技术上是否正确,但有时您只是需要更新嵌套对象。这是我使用钩子的解决方案。

setInputState ({ ... inputState, [括号]:内务部[括号],[childKey]: });

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

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

我非常认真地对待关于创建组件状态的完整副本的问题。说到这里,我强烈推荐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)}

使用箭头函数代替,这应该做的把戏。

setItems((prevState) => {
   prevState.nestedData = newNestedData;
   prevState.nestedData1 = newNestedData1;
});

不要忘记使用箭头函数(prevState) =>{更新js赋值语句…}

为了让事情变得通用,我研究了@ShubhamKhatri和@Qwerty的答案。

状态对象

this.state = {
  name: '',
  grandParent: {
    parent1: {
      child: ''
    },
    parent2: {
      child: ''
    }
  }
};

输入控件

<input
  value={this.state.name}
  onChange={this.updateState}
  type="text"
  name="name"
/>
<input
  value={this.state.grandParent.parent1.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent1.child"
/>
<input
  value={this.state.grandParent.parent2.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent2.child"
/>

updateState方法

setState作为@ShubhamKhatri的答案

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const oldstate = this.state;
  const newstate = { ...oldstate };
  let newStateLevel = newstate;
  let oldStateLevel = oldstate;

  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      newStateLevel[path[i]] = event.target.value;
    } else {
      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
      oldStateLevel = oldStateLevel[path[i]];
      newStateLevel = newStateLevel[path[i]];
    }
  }
  this.setState(newstate);
}

setState作为@Qwerty的答案

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const state = { ...this.state };
  let ref = state;
  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      ref[path[i]] = event.target.value;
    } else {
      ref = ref[path[i]];
    }
  }
  this.setState(state);
}

注意:上述方法不适用于数组