我试图通过使用这样的嵌套属性来组织我的状态:
this.state = {
someProperty: {
flag:true
}
}
但是像这样更新状态,
this.setState({ someProperty.flag: false });
是行不通的。怎样才能正确地做到这一点呢?
我试图通过使用这样的嵌套属性来组织我的状态:
this.state = {
someProperty: {
flag:true
}
}
但是像这样更新状态,
this.setState({ someProperty.flag: false });
是行不通的。怎样才能正确地做到这一点呢?
当前回答
把它写在一行里
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
其他回答
虽然你问的是基于类的React组件的状态,但useState钩子也存在同样的问题。更糟糕的是:useState钩子不接受部分更新。所以当useState钩子被引入时,这个问题就变得非常相关了。
我决定发布以下答案,以确保这个问题涵盖了使用useState钩子的更现代的场景:
如果你有:
const [state, setState] = useState({
someProperty: {
flag: true,
otherNestedProp: 1
},
otherProp: 2
})
你可以通过克隆当前数据并修补所需的数据段来设置嵌套属性,例如:
setState(current => { ...current,
someProperty: { ...current.someProperty,
flag: false
}
});
或者您可以使用Immer库来简化对象的克隆和修补。
或者你可以使用Hookstate库(免责声明:我是一名作家)来简单地管理复杂的(本地和全局)状态数据,并提高性能(阅读:不用担心渲染优化):
import { useHookstate } from '@hookstate/core'
const state = useHookstate({
someProperty: {
flag: true,
otherNestedProp: 1
},
otherProp: 2
})
获取要渲染的字段:
state.someProperty.flag.get()
// or
state.get().someProperty.flag
设置嵌套字段:
state.someProperty.flag.set(false)
下面是Hookstate示例,其中的状态被深度递归嵌套在树状数据结构中。
这是第一个答案的变体,不需要任何额外的包、库或特殊函数。
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
为了设置特定嵌套字段的状态,您已经设置了整个对象。为此,我创建了一个变量newState,并首先使用ES2015扩展操作符将当前状态的内容扩散到该变量中。然后,我用新值替换了this.state.flag的值(因为我在将当前状态扩展到对象后设置了flag: value,所以当前状态下的flag字段将被覆盖)。然后,我简单地将someProperty的状态设置为newState对象。
如果你想动态设置状态
下面的示例动态设置表单的状态,其中状态中的每个键都是object
onChange(e:React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
this.setState({ [e.target.name]: { ...this.state[e.target.name], value: e.target.value } });
}
这样就足够了,
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)
如果你正在使用ES2015,你可以访问Object.assign。您可以使用它来更新嵌套对象。
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
您将更新的属性与现有属性合并,并使用返回的对象更新状态。
编辑:将一个空对象作为目标添加到赋值函数中,以确保状态不会像carkod指出的那样直接发生突变。