Javascript 如何更新 React 中的嵌套状态属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43040721/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to update nested state properties in React
提问by Alex Yong
I'm trying to organize my state by using nested property like this:
我正在尝试使用这样的嵌套属性来组织我的状态:
this.state = {
someProperty: {
flag:true
}
}
But updating state like this,
但是像这样更新状态,
this.setState({ someProperty.flag: false });
doesn't work. How can this be done correctly?
不起作用。如何正确地做到这一点?
回答by Shubham Khatri
In order to setStatefor a nested object you can follow the below approach as I think setState doesn't handle nested updates.
为了setState嵌套对象,您可以遵循以下方法,因为我认为 setState 不处理嵌套更新。
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object
这个想法是创建一个虚拟对象对其执行操作,然后用更新的对象替换组件的状态
Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:
现在,展开运算符仅创建对象的一层嵌套副本。如果您的状态是高度嵌套的,例如:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
You could setState using spread operator at each level like
您可以在每个级别使用扩展运算符 setState ,例如
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helperpackage to update the state.
然而,随着状态变得越来越嵌套,上述语法变得越来越难看,因此我建议您使用immutability-helper包来更新状态。
See this answer on how to update state with immutability helper.
请参阅有关如何使用 更新状态的答案immutability helper。
回答by Yoseph
To write it in one line
写在一行
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
回答by Konstantin Smolyanin
Sometimes direct answers are not the best ones :)
有时直接的答案不是最好的:)
Short version:
精简版:
this code
这段代码
this.state = {
someProperty: {
flag: true
}
}
should be simplified as something like
应该简化为类似
this.state = {
somePropertyFlag: true
}
Long version:
长版:
Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.
目前你不应该想在 React 中使用嵌套状态。因为 React 不是面向嵌套状态的,所以这里提出的所有解决方案看起来都是 hacks。他们不使用框架,而是与之抗争。他们建议编写不太清晰的代码,以用于对某些属性进行分组的可疑目的。因此,它们作为挑战的答案非常有趣,但实际上毫无用处。
Lets imagine the following state:
让我们想象以下状态:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
What will happen if you change just a value of child1? React will not re-render the view because it uses shallow comparison and it will find that parentproperty didn't change. BTW mutating the state object directly is considered to be a bad practice in general.
如果只更改 的值会发生什么child1?React 不会重新渲染视图,因为它使用浅比较并且它会发现parent属性没有改变。顺便说一句,直接改变状态对象通常被认为是一种不好的做法。
So you need to re-create the whole parentobject. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.
所以你需要重新创建整个parent对象。但在这种情况下,我们会遇到另一个问题。React 会认为所有的孩子都改变了他们的价值观,并会重新渲染所有的孩子。当然,这对性能不利。
It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate()but I would prefer to stop here and use simple solution from the short version.
仍然可以通过编写一些复杂的逻辑来解决该问题,shouldComponentUpdate()但我更愿意在这里停止并使用简短版本的简单解决方案。
回答by Qwerty
Disclaimer
免责声明
Nested State in React is wrong design
Read this excellent answer.
React 中的嵌套状态是错误的设计
阅读这个优秀的答案。
?
?
Reasoning behind this answer:
React's setState is just a built-in convenience, but you soon realise that it has its limits. Using custom properties and intelligent use of forceUpdate gives you much more. eg:
class MyClass extends React.Component { myState = someObject inputValue = 42 ...MobX, for example, ditches state completely and uses custom observable properties.
Use Observables instead of state in React components.
这个答案背后的推理:
React 的 setState 只是一种内置的便利,但您很快就会意识到它有其局限性。使用自定义属性和智能使用 forceUpdate 可为您提供更多。例如:
class MyClass extends React.Component { myState = someObject inputValue = 42 ...例如,MobX 完全抛弃状态并使用自定义的可观察属性。
在 React 组件中使用 Observables 而不是 state。
?
?
the answer to your misery - see example here
你痛苦的答案 -见这里的例子
There is another shorterway to update whatever nested property.
还有另一种更短的方法来更新任何嵌套属性。
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
On one line
一行
this.setState(state => (state.nested.flag = false, state))
note: This here is Comma operator ~MDN, see it in action here (Sandbox).
注意:这里是逗号运算符 ~MDN,请在此处查看操作(沙盒)。
It is similar to (though this doesn't change state reference)
它类似于(尽管这不会改变状态引用)
this.state.nested.flag = false
this.forceUpdate()
For the subtle difference in this context between forceUpdateand setStatesee the linked example.
有关此上下文中forceUpdate和之间的细微差别,setState请参阅链接示例。
Of course this is abusing some core principles, as the stateshould be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok.
当然这是在滥用一些核心原则,因为它state应该是只读的,但是由于您立即丢弃旧状态并用新状态替换它,所以完全可以。
Warning
警告
Even though the component containing the state willupdate and rerender properly (except this gotcha), the props will failto propagate to children (see Spymaster's comment below). Only use this technique if you know what you are doing.
即使包含状态的组件将正确更新和重新渲染(除了这个 gotcha),道具也将无法传播给孩子(请参阅下面的 Spymaster 评论)。仅当您知道自己在做什么时才使用此技术。
For example, you may pass a changed flat prop that is updated and passed easily.
例如,您可以传递一个更改过的平面道具,该道具可以轻松更新和传递。
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
Now even though reference for complexNestedProp did not change (shouldComponentUpdate)
现在即使对 complexNestedProp 的引用没有改变(shouldComponentUpdate)
this.props.complexNestedProp === nextProps.complexNestedProp
the component willrerender whenever parent component updates, which is the case after calling this.setStateor this.forceUpdatein the parent.
每当父组件更新时,组件都会重新渲染,调用后this.setState或this.forceUpdate在父组件中就是这种情况。
Effects of mutating the state
改变状态的影响
Using nested stateand mutating the state directly is dangerous because different objects might hold (intentionally or not) different (older) references to the stateand might not necessarily know when to update (for example when using PureComponentor if shouldComponentUpdateis implemented to return false) ORare intended to display old data like in the example below.
使用嵌套状态并直接改变状态是危险的,因为不同的对象可能(有意或无意)持有对状态的不同(较旧)引用,并且可能不一定知道何时更新(例如,何时使用PureComponent或如果shouldComponentUpdate实现返回false)或者是旨在显示旧数据,如下例所示。
Imagine a timeline that is supposed to render historic data, mutating the data under the hand will result in unexpected behaviour as it will also change previous items.
想象一个应该呈现历史数据的时间线,改变手下的数据将导致意外行为,因为它也会改变以前的项目。
Anyway here you can see that Nested PureChildClassit not rerendered due to props failing to propagate.
无论如何,在这里您可以看到Nested PureChildClass由于道具无法传播而没有重新渲染。
回答by Alyssa Roose
If you are using ES2015 you have access to the Object.assign. You can use it as follows to update a nested object.
如果您使用的是 ES2015,您可以访问 Object.assign。您可以按如下方式使用它来更新嵌套对象。
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
You merge the updated properties with the existing and use the returned object to update the state.
您将更新后的属性与现有属性合并,并使用返回的对象来更新状态。
Edit: Added an empty object as target to the assign function to make sure the state isn't mutated directly as carkod pointed out.
编辑:向分配函数添加了一个空对象作为目标,以确保状态不会像 carkod 指出的那样直接改变。
回答by eyecandy.dev
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
回答by tokland
There are many libraries to help with this. For example, using immutability-helper:
有很多图书馆可以帮助解决这个问题。例如,使用immutability-helper:
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
Using lodash/fpset:
使用lodash/fp集:
import {set} from 'lodash/fp';
const newState = set(["someProperty", "flag"], false, this.state);
Using lodash/fpmerge:
使用lodash/fp合并:
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
回答by Joakim J?derberg
We use Immer https://github.com/mweststrate/immerto handle these kinds of issues.
我们使用 Immer https://github.com/mweststrate/immer来处理这类问题。
Just replaced this code in one of our components
刚刚在我们的一个组件中替换了此代码
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
With this
有了这个
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
With immer you handle your state as a "normal object". The magic happens behind the scene with proxy objects.
使用 immer,您可以将您的状态作为“正常对象”处理。魔法发生在代理对象的幕后。
回答by Matthew Berkompas
Here's a variation on the first answer given in this thread which doesn't require any extra packages, libraries or special functions.
这是该线程中给出的第一个答案的变体,它不需要任何额外的包、库或特殊功能。
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newStateand spreading the contents of the current state into it firstusing the ES2015 spread operator. Then, I replaced the value of this.state.flagwith the new value (since I set flag: valueafterI spread the current state into the object, the flagfield in the current state is overridden). Then, I simply set the state of somePropertyto my newStateobject.
为了设置特定嵌套字段的状态,您已经设置了整个对象。为此,我创建了一个变量,newState并首先使用 ES2015扩展运算符将当前状态的内容扩展到其中。然后,我将 的值替换为this.state.flag新值(因为我是flag: value在将当前状态传播到对象后设置的,所以当前状态中的flag字段被覆盖)。然后,我简单地将 的状态设置someProperty为我的newState对象。
回答by user2208124
Although nesting isn't really how you should treat a component state, sometimes for something easy for single tier nesting.
尽管嵌套并不是真正应该如何处理组件状态,但有时对于单层嵌套来说很容易。
For a state like this
对于这样的状态
state = {
contact: {
phone: '888-888-8888',
email: '[email protected]'
}
address: {
street:''
},
occupation: {
}
}
A re-useable method ive used would look like this.
我使用的可重复使用的方法看起来像这样。
handleChange = (obj) => e => {
let x = this.state[obj];
x[e.target.name] = e.target.value;
this.setState({ [obj]: x });
};
then just passing in the obj name for each nesting you want to address...
然后只需为您要解决的每个嵌套传递 obj 名称...
<TextField
name="street"
onChange={handleChange('address')}
/>


