[SalesForce] Lightning Web Component Error: [‘set’ on proxy: trap returned falsish for property ‘0’]

I am trying to build a cart where I add products to it and can then remove them. However on remove I receive the error ['set' on proxy: trap returned falsish for property '0'].

In my component ParentComponent, I select the products and add to an array cartItems. For each cart item a child component called cartRowItem is instantiated. See code below:

parentComponent.html:

<template>
    <div class="slds-grid slds-gutters">
            <lightning-card class="slds-col slds-size_1-of-3" title="Product Search">
                <div class="slds-grid slds-gutters">
                    <div class="slds-col slds-size_2-of-3 slds-var-m-left_small">
                        <lightning-input type="text" placeholder="Search for a product" value={productSearchKey} onchange={handleSearchKeyChange}></lightning-input>
                    </div>
                    
                    <div class="slds-col slds-size_1-of-3">
                        <button class="slds-button slds-button_brand slds-button_stretch slds-var-m-right_small" onclick={doSearch}>Search</button>
                    </div>
                </div>

                <template if:true={products}>
                    <lightning-datatable
                        key-field="id"
                        data={products}
                        columns={columns}
                        onrowaction={addToCart}
                        hide-checkbox-column>
                    </lightning-datatable>
                </template>
                
            </lightning-card>

        <lightning-card class="slds-col slds-size_2-of-3" title="Product Cart">
            <template for:each={cartItems} for:item="item">
                <c-cart-row-item key={item.id} cart-item={item} cart-items={cartItems} oncartitemremove={handleRemove}></c-cart-row-item>
            </template>
        </lightning-card>
    </div>
</template>

parentComponent.js:

import { LightningElement, api, track } from 'lwc';
import findProducts from '@salesforce/apex/TechDocSelfServeController.findProducts';

const tableColumns = [{
    label: 'Product Code',
    fieldName: 'ProductCode',
    type: 'text',
    sortable: true
},
{
    label: 'Product Name',
    fieldName: 'Name',
    type: 'text',
    sortable: 'true'
},
{
    label: 'Add to Cart',
    type: 'button-icon',
    
    typeAttributes: 
    {
        iconName: 'utility:add',
        name: 'addToCart',
        title: 'addToCart',
        disabled: false,
        alternativeText: 'Add to Cart',
    }
}];

export default class TechDocSelfServe extends LightningElement {
    products; 
    @api cartItems = [];
    columns = tableColumns;
    productSearchKey = '';
    error;
    
    doSearch() {
        let productSearchKey = this.productSearchKey;
        findProducts({ productSearchKey })
            .then((result) => {
                this.products = result;
                this.error = undefined;
            })
            .catch((error) => {
                this.error = error;
                this.products = undefined;
            });
    }

    addToCart(event) {
        const row = event.detail.row;
        this.cartItems = [...this.cartItems, row];
    
        const selectedEvent = new CustomEvent("cartitemadd", {
            detail: this.cartItems
        });

        console.log('cartItems on add -> ' + JSON.stringify(this.cartItems, null, 4));

        this.dispatchEvent(selectedEvent);
    }

    handleSearchKeyChange(event) {
        this.productSearchKey = event.target.value;
    }

    handleRemove(event) {
        this.cartItems = event.detail
    }
}

cartRowItem.html:

<template>
    <template if:true={displayRow}>
        <div class="slds-var-m-around_xx-small slds-box">
            <lightning-card>
                <div class="slds-text-heading_large slds-text-align_center "><u>{cartItem.Name}</u></div>
                    <lightning-button variant="destructive" label="Remove Item" title="RemoveItem" onclick={handleRemove} value={cartItem} class="slds-align_absolute-center"></lightning-button>
            </lightning-card>    
        </div>
    </template>
</template>

cartRowItem.js (updated as per Rahul comment):

import { LightningElement, api } from 'lwc';

export default class CartRowItem extends LightningElement {
    @api cartItem;
    _cartItems;
    displayRow = true;

    @api get cartItems() {
        return this._cartItems;
    }

    set cartItems(value) {
        this._cartItems = value;
    }

    handleRemove(event) {
        console.log('Should remove here.')
        var array = this._cartItems;
        var key = event.target.value.Id;
        var index = array.findIndex(function(item, i) {
            return item.Id === key
        });

        array.splice(index, 1);
        this._cartItems = array;

        console.log('cartItems after remove -> ' + JSON.stringify(this._cartItems, null, 4));
        
        const selectedEvent = new CustomEvent("cartitemremove", {
            detail: this._cartItems
        });

        this.dispatchEvent(selectedEvent);
        this.displayRow = false;
    }
}

When pressing Remove button in cartRowItem I will receive the error ['set' on proxy: trap returned falsish for property '0'] with the number at the end changing depending on what item in the array I am trying to remove.

Best Answer

The property set by the parent component is immutable, which mean's you can't modify that. You need to use the getter setter for the public property.

_cartItems;
...

@api
get cartItems(){
    return this._cartItems;
}

set cartItems(value){
    this._cartItems = JSON.parse(JSON.stringify(value));
}

Note that I have created new private property with different name i.e. _cartItems.

Now you can perform all the operations and update the _cartItems. To send updated data into parent, you need to dispatch a custom event from child.