[英]React setState - Add array to nested object with multiple arrays
我目前正在React中开发一个新的应用程序。 这是我第一次在React中创建东西。 该应用程序将显示我们自己的促销活动。
我的初始状态如下:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510558814960,
"endDateTimeStamp": 1510558814960,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
这是从我的defaultPromotion常量创建的。 这个常数存储在一个单独的文件中,我称之为api.js
export const defaultPromotion = {
name: '',
campaign: '',
url: 'https://',
position: 0,
periods: [
{
startDateTimeStamp: Date.now(),
endDateTimeStamp: Date.now(),
variants: [
{
title: '',
text: '',
image: '',
},
]
},
]
}
在我的createPromotion组件中,其创建过程如下
let promotionState = api.promotions.defaultPromotion;
this.state = {
promotion: promotionState
};
我可以添加以下内容的新期间:
addPromotion() {
let promotion = this.state.promotion;
promotion.periods.push( api.promotions.defaultPromotion.periods[0] );
this.forceUpdate();
}
此后,将按预期添加新的期间。 建议使用setState()做到这一点! 所以,我现在的新状态是:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
现在,我想为此促销期添加一个新的变体,这是我现在停留2天的地方。
我要添加一个新的期间,如下所示:
addVariant( periodKey ) {
const promotion = this.state.promotion;
promotion.periods[periodKey].variants.push(api.promotions.defaultPromotion.periods[0].variants[0]);
this.setState({ promotion: promotion });
}
periodKey在此处为“ 1”,因此,我希望为period [1]添加一个新的变体,但是,这两个周期都被添加了。 现在的状态如下:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
有人可以向我解释为什么会这样,以及如何以正确的方式添加新的变体吗?
非常感谢!
更新1
根据bennygenel和PatrickHübl-Neschkudla的回答,我的实现如下:
设置初始状态:
constructor(props) {
super(props);
let promotionState = api.promotions.defaultPromotion;
this.state = { ...promotionState };
}
方法:
addVariant( periodKey ) {
this.setState((prevState) => {
const { periods } = prevState;
periods[periodKey].variants.push(
Object.assign({}, { ...periods[periodKey].variants, api.promotions.defaultPromotion.periods[0].variants[0]})
);
return { periods };
});
}
但这仍然在所有时期都设置了新的变体。 我还尝试了Benny的确切代码,但结果相同。 该方法称为
this.props.addVariant( this.props.periodKey );
即使我这样称呼它:
this.props.addVariant(2);
发生相同的行为。
更新2
现在,我已将所有内容都重写为redux,因此我可以轻松地在每个组件中访问我的促销活动,而不必通过某些组件。 基于@mersocarlin的答案,我现在有以下减速器案例:
加期
case PROMOTION_ADD_PERIOD:
const { periods } = { ...state };
periods.push(api.promotions.defaultPromotion.periods[0]);
state = {
...state,
periods: periods
};
break;
添加期间变式
case PROMOTION_ADD_PERIOD_VARIANT :
state = {
...state,
periods: [
...state.periods[action.payload.period],
{
variants: [
...state.periods[action.payload.period].variants,
api.promotions.defaultPromotion.periods[0].variants[0]
]
}
]
};
break;
在以下情况下:添加一个新的变体,工作状态:
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510599968588,
"endDateTimeStamp": 1510599968588,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510599968594,
"endDateTimeStamp": 1510599968594,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
在那之后,添加一个新的变体,还可以,添加了该变体,但是我失去了第二个学期。 州:
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
我认为这是我看不到的小事情。 有人对“ PROMOTION_ADD_PERIOD_VARIANT”案例有解决方案吗?
更新3更改了“ PROMOTION_ADD_PERIOD”的情况,如下所示:
case PROMOTION_ADD_PERIOD:
state = {
...state,
periods: [
...state.periods,
initialState.periods[0]
]
};
break;
Update 4 Finaly找到了解决方案。 请参见下面的PROMOTION_ADD_PERIOD_VARIANT的最终代码:
state = {
...state,
periods: [
...state.periods.map((item, index) => {
if ( index !== action.payload.period ) {
return item;
}
return {
...item,
variants: [
...item.variants,
initialState.periods[0].variants[0]
]
}
})
]
};
非常感谢大家的帮助!
因此,这里发生的是您有一个数组,其中包含对同一对象的两个引用。
像这样想象:
myArray[0] = reference to defaultPromotion
myArray[1] = reference to defaultPromotion
这实际上是一个很好的例子,说明了为什么不变性概念在过去几年中得到了如此多的关注:)
您要在此处执行的操作不是将defaultPromotion对象添加到Promotions数组,而是创建一个具有与此对象相同道具的新对象并将其添加。 看起来像这样(取决于您的ES版本等)
promotion.periods.push( Object.assign({}, api.promotions.defaultPromotion.periods[0]) );
这样,您将创建一个新对象,并将其传递给数组,而不是对现有对象的引用。
而是销毁状态对象,并避免直接对其进行变异。 这也恰好是错误的模式。
每当您需要向阵列添加新项目时:
const state = { arrayProp: [{ prop1: 'prop1', prop2: 'prop2' }] } const newItem = { prop1: 'value1', prop2: 'value2', } const newState = { ...state, arrayProp: [ ...state.arrayProp, newItem, ] } console.log('newState', newState)
同样适用于您所在州内的嵌套属性: Redux也使用这种方法
const state = { objectProp: { arrayPropWithinArray: [ { id: '0', otherProp: 123, yetAnotherProp: 'test' }, { id: '1', otherProp: 0, yetAnotherProp: '' } ] } } const { objectProp } = state const index = objectProp.arrayPropWithinArray.findIndex(obj => obj.id === '1') const newSubItem = { otherProp: 1, yetAnotherProp: '2', } const newState = { ...state, objectProp: { ...objectProp, arrayPropWithinArray: [ ...objectProp.arrayPropWithinArray.slice(0, index), { ...objectProp.arrayPropWithinArray[index], ...newSubItem, }, ...objectProp.arrayPropWithinArray.slice(index + 1), ] } } console.log('newState', newState)
您的具体情况( 如您的评论中所述 )
const periodKey = '2' // your periodKey var. Get it from the right place, it can be your action for example
const index = state.periods.findIndex(period => period.id === periodKey) // find which index has to be updated
state = {
...state, // propagates current state
periods: [
...state.periods.slice(0, index), // propagates everything before index
{
...state.periods[index],
variants: [
...state.periods[index].variants,
api.promotions.defaultPromotion.periods[0].variants[0],
],
},
...state.periods.slice(0, index + 1) // propagates everything after index
]
}
第一个建议是,如果您的状态中只有一个升级对象而不是一个数组,则失去升级级别。 这将减少您的状态的复杂性。 您可以使用传播语法轻松设置初始状态。
例
let promotionState = api.promotions.defaultPromotion;
this.state = { ...promotionState };
上面的代码最终将创建如下所示的状态;
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [{
"title": "",
"text": "",
"image": ""
}]
}, {
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [{
"title": "",
"text": "",
"image": ""
}]
}]
}
我可以提出的另一个建议是使用功能性setState
来减少突变的可能性。
例
addPromotion() {
this.setState((prevState) => {
const { periods } = prevState;
periods.push(api.promotions.defaultPromotion.periods[0]);
return { periods };
});
}
addVariant( periodKey ) {
this.setState((prevState) => {
const { periods } = prevState;
periods[periodKey].variants.push(api.promotions.defaultPromotion.periods[0].variants[0]);
return { periods };
});
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.