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

问题描述 投票:2回答:4

我需要修改我的状态,我不确定如何正确地做到这一点。

我州的帐户资产看起来像这样:

{     
 "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并将其添加到帐户状态对象内的targetCategoryId。

reactjs react-redux
4个回答
1
投票

是的,你不应该在减速机上做state.foo = 'bar'。来自redux docs

我们不会改变国家。我们用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] }
              }
        }, {});
      };
  }
}

但这是一种痛苦......你应该尝试将normalize your data变成扁平阵列。


0
投票
// first, let's clean up the action a bit
// type and "payload". I like the data wrapped up in a bundle with a nice
// bow on it. ;) If you don't like this, just adjust the code below. 
dispatch({
  type: Constants.MOVE_PRODUCT,
  payload: { 
      product: { productId: 54 }
      sourceCategoryId: 7,
      targetCategoryId: 9
  }
});

// destructure to get our id and categories from state
const { id, categories } = state

// map the old categories to a new array
const adjustedCategories = categories.map(cat => {
    // destructure from our payload
    const { product, sourceCategoryId, targetCategoryId } = action.payload

    // if the category is the "moving from" category, filter out the product
    if (cat.id === sourceCategoryId) {
        return { id: cat.id, products: [...cat.products.filter(p => p.productId !== product.productId)
    }

    // if the category is our "moving to" category, use the spread operator and add the product to the new array
    if (cat.id === targetCategoryId) {
        return { id: cat.id, products: [...cat.products, product] }
    }
)

// construct our new state
return { id, categories: adjustedCategories }

这个解决方案保持功能纯净,应该给你你想要的。它没有经过测试,因此可能并不完美。


0
投票

您可以采取以下方法:

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 };
  }
}

0
投票

这是丑陋的解决方案:)

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 } };
  }
};

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

这是工作示例:

const action = {
  productId: 54,
  sourceCategoryId: 7,
  targetCategoryId: 9,
}

const state = {     
 "account":{  
    "id":7,    
    "categories":[  
       {  
          "id":7,          
          "products":[  
             {                  
                "productId":54,             
             },
             {                  
                "productId":67,             
             },
          ]
       },
       {  
          "id":9,          
          "products":[  
             {                  
                "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.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 }
);

const newState = { ...state, account: { ...state.account, categories: newCategories } };

console.log( newState );
© www.soinside.com 2019 - 2024. All rights reserved.