我正在尝试使用这样的嵌套属性来组织我的状态:
this.state = {
someProperty: {
flag:true
}
}
但是像这样更新状态,
this.setState({ someProperty.flag: false });
不起作用。如何正确地做到这一点?
我正在尝试使用这样的嵌套属性来组织我的状态:
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
包来更新状态。
请参阅有关如何使用 更新状态的答案immutability-helper
。
写在一行
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
有时直接的答案不是最好的:)
精简版:
这段代码
this.state = {
someProperty: {
flag: true
}
}
应该简化为类似
this.state = {
somePropertyFlag: true
}
长版:
目前你不应该想在 React 中使用嵌套状态。因为 React 不是面向嵌套状态的,所以这里提出的所有解决方案看起来都是 hacks。他们不使用框架,而是与之抗争。他们建议编写不太清晰的代码,以用于对某些属性进行分组的可疑目的。因此,它们作为挑战的答案非常有趣,但实际上毫无用处。
让我们想象以下状态:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
如果只更改 的值会发生什么child1
?React 不会重新渲染视图,因为它使用浅比较并且它会发现parent
属性没有改变。顺便说一句,直接改变状态对象通常被认为是一种不好的做法。
所以你需要重新创建整个parent
对象。但在这种情况下,我们会遇到另一个问题。React 会认为所有的孩子都改变了他们的value观,并会重新渲染所有的value观。当然,这对性能不利。
仍然可以通过编写一些复杂的逻辑来解决该问题,shouldComponentUpdate()
但我更愿意在这里停止并使用简短版本的简单解决方案。
React 中的嵌套状态是错误的设计
阅读这个优秀的答案。
这个答案背后的推理:
React 的 setState 只是一种内置的便利,但您很快就会意识到它有其局限性。使用自定义属性和智能使用
forceUpdate
为您提供更多。例如:class MyClass extends React.Component { myState = someObject inputValue = 42 ...
例如,MobX 完全抛弃状态并使用自定义的可观察属性。
在 React 组件中使用 Observables 而不是 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
请参阅链接示例和沙箱。
当然,这是在滥用一些核心原则,因为它state
应该是只读的,但是由于您立即丢弃旧状态并用新状态替换它,所以完全可以。
即使包含状态的组件将正确更新和重新渲染(除了这个 gotcha),props也将无法传播给孩子(请参阅下面的 Spymaster 评论)。仅当您知道自己在做什么时才使用此技术。
例如,您可以传递一个更改过的平面props,该props可以轻松更新和传递。
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
)或者是旨在显示旧数据,如下例所示。
想象一个应该呈现历史数据的时间线,改变手下的数据将导致意外行为,因为它也会改变以前的项目。
无论如何,在这里您可以看到Nested PureChildClass
由于props无法传播而没有重新渲染。
如果您使用的是 ES2015,您可以访问 Object.assign。您可以按如下方式使用它来更新嵌套对象。
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
您将更新后的属性与现有属性合并,并使用返回的对象来更新状态。
编辑:向分配函数添加了一个空对象作为目标,以确保状态不会像 carkod 指出的那样直接改变。