繁体   English   中英

React setState-将数组添加到具有多个数组的嵌套对象

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM