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

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

但是像这样更新状态,

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

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


当前回答

有很多库可以提供帮助。例如,使用immutability-helper:

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

使用lodash/fp设置:

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

使用lodash/fp合并:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});

其他回答

你可以用对象展开来做这个 代码:

 this.setState((state)=>({ someProperty:{...state.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更新状态,请参阅此回答。

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

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

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

免责声明

React中的嵌套状态是错误的设计 阅读这篇精彩的回答。

 

这个答案背后的原因是: React的setState只是一个内置的方便,但你很快就会意识到 它有它的局限性。使用自定义属性和智能使用 forceUpdate提供更多信息。 例如: MyClass扩展React。组件{ myState = someObject inputValue = 42 … 例如,MobX完全抛弃状态,使用自定义可观察属性。 在React组件中使用observable而不是state。

 


你痛苦的答案——请看这里的例子

还有另一种更短的方法来更新任何嵌套的属性。

this.setState(state => {
  state.nested.flag = false
  state.another.deep.prop = true
  return state
})

在一行上

 this.setState(state => (state.nested.flag = false, state))

注意:这里是逗号操作符~MDN,在这里(沙盒)看到它的作用。

它类似于(尽管这不会改变状态引用)

this.state.nested.flag = false
this.forceUpdate()

关于这个上下文中forceUpdate和setState之间的细微差别,请参阅链接示例和沙盒。

当然,这是在滥用一些核心原则,因为状态应该是只读的,但是由于您立即丢弃了旧状态并用新状态替换它,所以这是完全可以的。

警告

即使包含状态的组件将正确更新和重新渲染(除了这个gotcha),道具将无法传播给孩子(见Spymaster的评论下面)。只有当你知道你在做什么时才使用这个技巧。

例如,您可以传递一个已更改的平面道具,该道具已更新并易于传递。

render(
  //some complex render with your nested state
  <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)

现在即使complexNestedProp的引用没有改变(shouldComponentUpdate)

this.props.complexNestedProp === nextProps.complexNestedProp

每当父组件更新时,组件都会重新呈现,这是在调用this之后的情况。setState或this。在父类中执行forceUpdate。

改变状态沙箱的效果

使用嵌套状态和直接改变状态是危险的,因为不同的对象可能(有意或无意地)持有对状态的不同(旧的)引用,并且可能不一定知道何时更新(例如使用PureComponent时,或者如果shouldComponentUpdate被实现以返回false)或意图显示旧数据,如下例所示。

想象一下,一个应该呈现历史数据的时间轴,改变手上的数据将导致意想不到的行为,因为它也会改变之前的项目。

无论如何,在这里你可以看到嵌套的PureChildClass没有被重新渲染,因为道具未能传播。

有很多库可以提供帮助。例如,使用immutability-helper:

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

使用lodash/fp设置:

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

使用lodash/fp合并:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});