Search code examples
arraysreactjsarray-splice

Removing specific index from array in React


I'm using this function to remove an item from my state array in React

removeProduct(index) {
    this.setState(prevState => ({
        selectedProducts: update(prevState.selectedProducts, {$splice: [[index, 1]]}),
    }))
}             

it is being passed through like this:

<Basket items={this.state.selectedProducts} removeItem={this.removeProduct.bind(this)}/>

<BasketItem product={product} key={index} remove={this.props.removeItem}/>

and then called like this:

<button onClick={props.remove.bind(this)}>x</button>

but its not removing that specific item. Its only removing the first item in the array.

Can anyone help?


Solution

  • From your BasketItem (or wherever the button is) you need to lift a unique identifier up to the removeProduct function. I'm assuming the removeProduct is somewhere in a parent of BasketItem.

    When the button is clicked, BasketItem's onRemoveProduct is called. That in turn calls it's prop with the id of the the item. The parent (Basket) onRemoveProduct then knows what product to remove the basket.

    See code below.

    Note: Do not use the index from .map as the key. You need to use some identifying item on the product.

    Example: * you have 3 items with index and key = (0,1,2).

    • React renders the 3 items

    • You remove the 2nd item (key = 1) and then the array.map happens again.

    • It returns 2 items with keys = (0,1).

    • React sees that the items have changed and that the item with key = 2 (the last item) is missing.

    • Item 2 (the last item, is removed, leaving the first two in place.

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          products: [
            {
              id: 0,
              name: "Product 1"
            },
            {
              id: 1,
              name: "Product 2"
            },
            {
              id: 2,
              name: "Product 3"
            }
          ]
        };
        this.handleRemoveProduct = this.handleRemoveProduct.bind(this);
      }
    
      handleRemoveProduct(e) {
        const products = this.state.products.filter(prod => prod.id !== e)
        this.setState({products})
      }
    
      render() {
        return (
          <div>
            <ul>
              {this.state.products.map((product, index) => {
                return (
                  <BasketItem
                    key={product.id}
                    product={product}
                    onRemoveProduct={this.handleRemoveProduct}
                  />
                );
              })}
            </ul>
          </div>
        );
      }
    }
    
    class BasketItem extends React.Component {
      constructor(props) {
        super(props);
        this.onRemoveProduct = this.onRemoveProduct.bind(this);
      }
      onRemoveProduct(e) {
        e.preventDefault();
        this.props.onRemoveProduct(this.props.product.id)
      }
      
      render() {
        return (
          <li>
            {this.props.product.name}
            <button onClick={this.onRemoveProduct}>X</button>
          </li>
        );
      }
    }
    
    ReactDOM.render(<App />, document.getElementById("root"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>