Reactjs 当我必须遍历和查找/删除状态中的项时,如何安全地更新状态

Reactjs 当我必须遍历和查找/删除状态中的项时,如何安全地更新状态,reactjs,react-redux,Reactjs,React Redux,我需要修改我的状态,但我不确定如何正确执行 我所在州的帐户属性如下所示: { "account":{ "id":7, "categories":[ { "id":7, "products":[ { "productId":54

我需要修改我的状态,但我不确定如何正确执行

我所在州的帐户属性如下所示:

{     
 "account":{  
    "id":7,    
    "categories":[  
       {  
          "id":7,          
          "products":[  
             {                  
                "productId":54                
             }
          ]
       },
       {  
          "id":9,          
          "products":[  
             {                  
                "productId":89                
             }
          ]
       }
    ]
 }
}
我的行动发出以下信息:

dispatch({
  type: Constants.MOVE_PRODUCT,
  productId: 54,
  sourceCategoryId: 7,
  targetCategoryId: 9
});
现在我的减速器骨架是:

const initialState = {
  account: null,
};


const accounts = (state = initialState, action) => {
  switch (action.type) {
    case Constants.MOVE_PRODUCT:
      /*
      action.productId
      action.sourceCategoryId
      action.targetCategoryId
      */

      const sourceCategoryIndex = state.account.categories.findIndex((category) => { return category.id === action.sourceCategoryId; });
      const sourceCategory = state.account.categories[sourceCategoryIndex];

      const targetCategoryIndex = state.account.categories.findIndex((category) => { return category.id === action.targetCategoryId; });
      const targetCategory = state.account.categories[targetCategoryIndex];

    // ??

      return {...state};
  }
}

export default accounts;
我很困惑,如果我直接在开关块内部更新状态,这是错误的吗

它是否必须是一个线性更新,在适当的地方进行突变,或者只要我在开关块中进行突变,就可以了

更新

在操作中,我需要从sourceCategoryId中删除productId,并将其添加到account state对象内部的targetCategoryId中


这个解决方案保持了函数的纯粹性,应该满足您的需要。它没有经过测试,所以可能并不完美

您可以采取以下方法:

const accounts = (state = initialState, action) => {
  switch (action.type) {
    case Constants.MOVE_PRODUCT:

      // Extract action parameters
      const { productId, sourceCategoryId, targetCategoryId } = action

      // Manually "deep clone" account state
      const account = {
        id : state.account.id,
        categories : state.account.categories.map(category => ({          
          id : category.id,
          products : category.products.map(product => ({ productId : product.productId })
        }))
      }

      // Extract source and target categories
      const sourceCategory = account.categories.find(category => category.id === sourceCategoryId);
      const targetCategory = account.categories.find(category => category.id === targetCategoryId);

      if(sourceCategory && targetCategory) {

        // Find product index
        const index = sourceCategory.products.findIndex(product => (product.productId === action.productId))
        if(index !== -1) {

          const product = sourceCategory.products[index]

          // Remove product from source category
          sourceCategory.products.splice(index, 1) 

          // Add product to target category
          targetCategory.products.splice(index, 0, product) 
        }
      }

      return { account };
  }
}

是的,您不应该在减速机中执行
state.foo='bar'
。从:

我们不会改变国家。我们使用
Object.assign()
创建一个副本
Object.assign(state,{visibilityFilter:action.filter})
也是错误的:它将改变第一个参数。必须提供一个空对象作为第一个参数。您还可以启用对象扩展运算符建议,改为写入
{…state,…newState}

所以你的减速器看起来像

function accountsReducer (state = initialState, { sourceCategoryId, productId }) {
  const targetProduct = state.categories
    .find(({ id }) => id === sourceCategoryId)
    .products
    .find(({ id }) => id === productId);

  switch (action.type) {
    case Constants.MOVE_PRODUCT:
      return {
        ...state,
        categories: state.categories.reduce((acc, cat) => {
          return cat.id !== sourceCategoryId
            ? {
                ...acc,
                cat: { ...cat, products: cat.products.filter(({ id }) => id !== productId) }
              }
            : {
                ...acc,
                cat: { ...cat, products: [...cat.products, targetProduct] }
              }
        }, {});
      };
  }
}

但这是一种痛苦……您应该尝试将其放入平面阵列中。

这是一个丑陋的解决方案:)

Phew:)作为一名学习者,这对我很好:)但是,我喜欢@Daniel Lizik的方法,使用
reduce

以下是工作示例:

const action={
产品编号:54,
资料来源类别ID:7,
目标类别ID:9,
}
常量状态={
“帐户”:{
“id”:7,
“类别”:[
{  
“id”:7,
“产品”:[
{                  
“productId”:54,
},
{                  
“productId”:67,
},
]
},
{  
“id”:9,
“产品”:[
{                  
“productId”:89,
}
]
}
]
}
};
const sourceCategoryIndex=state.account.categories.findIndex(el=>el.id==action.sourceCategoryId);
const targetCategoryIndex=state.account.categories.findIndex(el=>el.id==action.targetCategoryId);
const sourceCategory=state.account.categories.find(el=>el.id==action.sourceCategoryId);
const targetCategory=state.account.categories.find(el=>el.id==action.targetCategorid);
const itemToMove=sourceCategory.products.find(el=>el.productId==action.productId);
const newSourceCategory={…sourceCategory,products:sourceCategory.products.filter(el=>el.productId!==action.productId)};
const newTargetCategory={…targetCategory,products:[…targetCategory.products,itemToMove]};
const newCategories=Object.assign([],state.account.categories,{[SourceCategorinIndex]:NewSourceCategority,
[targetCategoryIndex]:新目标类别}
);
const newState={…state,account:{…state.account,categories:newCategories};

console.log(newState)不应直接更新状态或对其进行变异。除非你这样做,否则你可以在你的案件中做任何事情。您想如何更新您的状态?我看不懂你的行为,也许我不清楚。但是,如果你提供更多的细节,这将是有用的。移动产品,但移动的产品(来源)会发生什么?删除产品或其他什么东西?@ DeSerCKIN是的,从SoeCeCeCordRyID中删除并添加到TaleCaseRyIDID。考虑规范化您的状态以看起来更像关系数据库,它将消除遍历状态树(因为它现在是平的)的需要。是的,作为其当前形状,很难更新它。我正在研究它,我将提供一个解决方案,但它非常难看:)感谢那个规范化链接,我将研究它。顺便说一句,我如何在我当前的switch语句中使用这个reducer?初始状态没有类别,它有帐户。所以我们需要另一个关卡,对吗?在“返回”中{…state,state.account.categories:``part?但这不会返回帐户。在调用
createStore
的地方发布代码。在我看来,你应该在
初始状态中实例化一个空的
帐户。categories
数组,这样你就不必检查数组是否存在。
function accountsReducer (state = initialState, { sourceCategoryId, productId }) {
  const targetProduct = state.categories
    .find(({ id }) => id === sourceCategoryId)
    .products
    .find(({ id }) => id === productId);

  switch (action.type) {
    case Constants.MOVE_PRODUCT:
      return {
        ...state,
        categories: state.categories.reduce((acc, cat) => {
          return cat.id !== sourceCategoryId
            ? {
                ...acc,
                cat: { ...cat, products: cat.products.filter(({ id }) => id !== productId) }
              }
            : {
                ...acc,
                cat: { ...cat, products: [...cat.products, targetProduct] }
              }
        }, {});
      };
  }
}
const accounts = (state = initialState, action) => {
  switch (action.type) {
    case Constants.MOVE_PRODUCT:
      const sourceCategoryIndex = state.account.categories.findIndex(
        el => el.id === action.sourceCategoryId
      );

      const targetCategoryIndex = state.account.categories.findIndex(
        el => el.id === action.targetCategoryId
      );

      const sourceCategory = state.account.categories.find(
        el => el.id === action.sourceCategoryId
      );

      const targetCategory = state.account.categories.find(
        el => el.id === action.targetCategoryId
      );

      const itemToMove = sourceCategory.products.find(
        el => el.productId === action.productId
      );

      const newSourceCategory = {
        ...sourceCategory,
        products: sourceCategory.products.filter(
          el => el.productId !== action.productId
        )
      };

      const newTargetCategory = {
        ...targetCategory,
        products: [...targetCategory.products, itemToMove]
      };

      const newCategories = Object.assign([], state.account.categories, {
        [sourceCategoryIndex]: newSourceCategory,
        [targetCategoryIndex]: newTargetCategory
      });

      return { ...state, account: { ...state.account, categories: newCategories } };
  }
};