我试图通过使用这样的嵌套属性来组织我的状态:
this.state = {
someProperty: {
flag:true
}
}
但是像这样更新状态,
this.setState({ someProperty.flag: false });
是行不通的。怎样才能正确地做到这一点呢?
我试图通过使用这样的嵌套属性来组织我的状态:
this.state = {
someProperty: {
flag:true
}
}
但是像这样更新状态,
this.setState({ someProperty.flag: false });
是行不通的。怎样才能正确地做到这一点呢?
当前回答
还有两个选项没有提到:
如果您有深度嵌套的状态,请考虑是否可以重新构造子对象,使其位于根节点。这使得数据更容易更新。 Redux文档中列出了许多方便的库来处理不可变状态。我推荐Immer,因为它允许您以可变的方式编写代码,但在幕后处理必要的克隆。它还会冻结生成的对象,这样以后就不会意外地改变它。
其他回答
我看到每个人都给出了基于类的组件状态更新解决方案,这是预期的,因为他要求,但我试图给钩子相同的解决方案。
const [state, setState] = useState({
state1: false,
state2: 'lorem ipsum'
})
现在,如果你想改变嵌套对象键state1,那么你可以做以下任何一项:
过程1
let oldState = state;
oldState.state1 = true
setState({...oldState);
流程2
setState(prevState => ({
...prevState,
state1: true
}))
我最喜欢过程。
为了让事情变得通用,我研究了@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);
}
注意:上述方法不适用于数组
这样就足够了,
const isObject = (thing) => {
if(thing &&
typeof thing === 'object' &&
typeof thing !== null
&& !(Array.isArray(thing))
){
return true;
}
return false;
}
/*
Call with an array containing the path to the property you want to access
And the current component/redux state.
For example if we want to update `hello` within the following obj
const obj = {
somePrimitive:false,
someNestedObj:{
hello:1
}
}
we would do :
//clone the object
const cloned = clone(['someNestedObj','hello'],obj)
//Set the new value
cloned.someNestedObj.hello = 5;
*/
const clone = (arr, state) => {
let clonedObj = {...state}
const originalObj = clonedObj;
arr.forEach(property => {
if(!(property in clonedObj)){
throw new Error('State missing property')
}
if(isObject(clonedObj[property])){
clonedObj[property] = {...originalObj[property]};
clonedObj = clonedObj[property];
}
})
return originalObj;
}
const nestedObj = {
someProperty:true,
someNestedObj:{
someOtherProperty:true
}
}
const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true
console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty)
对于在2022年读书的人来说:
constructor(props) {
super(props);
this.state = {
someProperty: {
flag: true
}
otherValues: '',
errors: {}
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
const someProperty = { ...this.state.someProperty };
someProperty[name] = value;
this.setState({
someProperty: someProperty
});
}
.......
有时候直接回答并不是最好的答案:)
短版:
这段代码
this.state = {
someProperty: {
flag: true
}
}
应该简化成这样吗
this.state = {
somePropertyFlag: true
}
长版:
目前你不应该在React中使用嵌套状态。因为React不是面向嵌套状态的,这里提出的所有解决方案看起来都是hack。他们不使用框架,而是与之斗争。他们建议为了分组某些属性的可疑目的而编写不那么清晰的代码。所以它们作为挑战的答案是非常有趣的,但实际上毫无用处。
让我们想象一下下面的状态:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
如果只改变child1的值会发生什么?React不会重新渲染视图,因为它使用浅比较,它会发现父属性没有改变。顺便说一句,直接改变状态对象通常被认为是一种糟糕的做法。
所以你需要重新创建整个父对象。但在这种情况下,我们会遇到另一个问题。React会认为所有的孩子都改变了他们的价值观,并重新渲染所有的孩子。当然,这对性能没有好处。
通过在shouldComponentUpdate()中编写一些复杂的逻辑仍然可以解决这个问题,但我宁愿在这里停下来,使用简短版本中的简单解决方案。