Joseph
Joseph

Reputation: 7755

Delete Item inside an Array of an Array in React and Redux

I have an item inside an array of an array and I want to target it and delete it. My problem is how do I access it and delete that particular item without mutating it.

Codesandbox is here

CLICK HERE

case appConstants.DELETE_IMAGE_SUCCESS:
  return {
    ...state,
    products: state.productImages.filter((item) => item !== action.payload)
  };

Upvotes: 0

Views: 110

Answers (3)

Amila Senadheera
Amila Senadheera

Reputation: 13225

You need to pass productIndex or id along with the imageFileName to correctly do the delete. Otherwise, all the products having the same imageFileName will be deleted.

Try this

 case appConstants.DELETE_IMAGE_SUCCESS:
      return {
        ...state,
        products: (state.products || []).map((item, itemIndex) => {
          if (itemIndex === action.payload.productIndex) {
            return {
              ...item,
              productImages: item.productImages.filter(
                (image) => image.imageFileName !== action.payload.imageFileName
              )
            };
          }
          return item;
        })
      };

In App.js file

  const onDeleteImage = (productIndex, imageFileName) => {
    dispatch(deleteImage(productIndex, imageFileName));
  };

in actions index.js file

export const deleteImage = (productIndex, imageFileName) => {
  return {
    type: appConstants.DELETE_IMAGE_SUCCESS,
    payload: {
      productIndex,
      imageFileName
    }
  };
};

In MediaCard.js file

 <Button
          type="button"
          color="primary"
          variant="contained"
          onClick={() => onDeleteImage(productIndex, data.imageFileName)}
        >

Code sandbox => https://codesandbox.io/s/redux-added-array-of-object-inside-another-aray-in-react-forked-xmghr?file=/src/reducers/productReducer.js

Upvotes: 0

Lin Du
Lin Du

Reputation: 102207

You should pass an additional payload - product. So that we can find the target product in state.products array by productCode. Suppose ProductCode can Identity a product.

Only use imageFileName is unable to determine which product it belongs to.

case appConstants.DELETE_IMAGE_SUCCESS:
      console.log(state);
      const nState = {
        ...state,
        products: state.products.map((item) => {
          if (item.productCode !== action.payload.product.productCode)
            return item;
          return {
            ...item,
            productImages: item.productImages.filter(
              (v) => v.imageFileName !== action.payload.imageFileName
            )
          };
        })
      };
      console.log(nState);
      return nState;

App.js:

// ...
const onDeleteImage = (imageFileName, product) => {
    dispatch(deleteImage({ imageFileName, product }));
  };

  return (
    <div className="App">
      {(products || []).map((product, index) => (
        <ProductCard
          product={product}
          key={index}
          onDeleteImage={(imageFileName) =>
            onDeleteImage(imageFileName, product)
          }
        />
      ))}
    </div>
  );
// ...

CodeSandbox

Upvotes: 1

jsejcksn
jsejcksn

Reputation: 33691

Here's the conceptual solution:

case appConstants.DELETE_IMAGE_SUCCESS:
  return {
    ...state,
    products: state.products.map(product => ({
      ...product,
      productImages: (product.productImages ?? [])
        .filter(({imageFileName}) => imageFileName !== action.payload),
    })),
  };

Caveat: if more than one product has an image with the same file name as the one you are deleting, it will be deleted from the other products as well. (This is currently a limitation of the information provided in your payload.) If that description is a bug for your program, then you'll need to also provide a product ID in the payload so that you only filter images for the matching product.

Upvotes: 0

Related Questions