Umbro
Umbro

Reputation: 2204

How delete an object based on its index from a nested array in React?

I click the icon 'delete' on the product. I pull out his index and save it in state. Example: select: 1,index: 1. How to set this.setState to delete an object nested in an array colors in array products. Example delete object:

{
  a: 'orange'
} 

from array colors in array products

this.state.select is the item in the products, this.state.index is color in the item to delete

And how does it look in a real application? Give your products and colors id? I would like it to be dynamic. I click the product, download its index and delete

class App extends Component {
  constructor(){
    super();

    this.state {
      products: [  
            {
                colors: [{a:'black'}, {a:'orange'}, {a:'purple'}]
                desc: 'gfgfg'
            },
            {
                colors: [{a: 'yellow'}, {a: 'white'}, {a:'gray'}],
                desc: 'gfgfgfg'
            },
            {
                colors: [{a: 'pink'}, {a: 'brown'}, {a:'green'}],
                desc: 'gfgfgfg'
            }
        ],
        select: 1 //example
        index: 1 //example
    }

  }

  removeItem = () => {
    const { select, index } = this.state;

    if(index) {
      this.setState({
          products: [
            ...this.state.products[select].colors.slice(0, index),
            ...this.state.products[select].colors.slice(index + 1),
          ]
      });
    }
  };


  render () {

    return (
      <div>
        <ul>
            {
                this.state.products
                .map((product, index) =>
                    <Product
                        key={index}
                        index={index}
                        product={product}
                    />
                )
            }
        </ul>
          <Products

          />
      </div>
    )
  } 
}

Upvotes: 3

Views: 2349

Answers (2)

Junius L
Junius L

Reputation: 16122

You need to pass the remove function to Product component, in Product component pass the select and index to removeItem function.

modify your remove item, to take in two parameters, select and index.

removeItem = (select, index) => {

  const filtered = this.state.products[select].colors.filter(
    (color, i) => i !== index
  );

  this.setState(prevState => {
    return {
      select: select,
      index: index,
      products: [
        ...prevState.products.slice(0, select),
        Object.assign({}, prevState.products[select], { colors: filtered }),
        ...prevState.products.slice(select + 1)
      ]
    };
  });
};

Pass the function as prop to Product component.

<div>
  <ul>
    {this.state.products.map((product, index) => (
      <Product
        key={index}
        index={index}
        removeItem={this.removeItem}
        product={product}
      />
    ))}
  </ul>
</div>

In your product component, pass the index of the color and the select.

<button onClick={() => removeItem(index, i)}>X</button>

DEMO

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>

<div id="root"></div>

<script type="text/babel">

class Product extends React.Component {
  render() {
    const { product, removeItem, index } = this.props;
    return (
      <div>
        <p>{product.desc}</p>
        <ul>
          {product.colors.map((color, i) => (
            <li>
              {color.a} <button onClick={() => removeItem(index, i)}>X</button>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      name: "React",
      products: [
        {
          colors: [{ a: "black" }, { a: "orange" }, { a: "purple" }],
          desc: "gfgfg"
        },
        {
          colors: [{ a: "yellow" }, { a: "white" }, { a: "gray" }],
          desc: "gfgfgfg"
        },
        {
          colors: [{ a: "pink" }, { a: "brown" }, { a: "green" }],
          desc: "gfgfgfg"
        }
      ],
      select: 1, //example
      index: 1 //example
    };
  }

    removeItem = (select, index) => {
      const filtered = this.state.products[select].colors.filter(
        (color, i) => i !== index
      );

      this.setState(prevState => {
        return {
          select: select,
          index: index,
          products: [
            ...prevState.products.slice(0, select),
            Object.assign({}, prevState.products[select], { colors: filtered }),
            ...prevState.products.slice(select + 1)
          ]
        };
      });
    };

  render() {
    return (
    <div>
      <ul>
        {this.state.products.map((product, index) => (
          <Product
            key={index}
            index={index}
            removeItem={this.removeItem}
            product={product}
          />
        ))}
      </ul>
    </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
</script>

Upvotes: 1

Shilly
Shilly

Reputation: 8589

Just use Array.filter() to create a new array not containing the specific element. So your new color array for that desc is:

this.state.products[select].colors.filter( color => color.a !== 'orange' )

This keeps all the colors where a is not orange. Keep in mind that arrays are zero-based, so your select and index should both start at 0.

If you do not want to store the actual indexes, use the desc instead.

So if select would be gfgfg instead of the array index, you could do:

const state = {
  products: [
      {
          colors: [{a:'black'}, {a:'orange'}, {a:'purple'}],
          desc: 'gfgfg'
      },
      {
          colors: [{a: 'yellow'}, {a: 'white'}, {a:'gray'}],
          desc: 'gfgfgfg'
      },
      {
          colors: [{a: 'pink'}, {a: 'brown'}, {a:'green'}],
          desc: 'gfgfgfg'
      }
  ],
  selected_product: 'gfgfg',
  color: 'orange'
};

const setState = update => Object.assign( state, update );

console.log( 'before update' );
console.log( JSON.stringify( state.products[0] ));

setState({
  products: state.products.map( product => {
    if ( product.desc === state.selected_product ) {
      product.colors = product.colors.filter( color => color.a !== state.color );
    }
    return product;
  })
});

console.log( 'after update' );
console.log( JSON.stringify( state.products[0] ));

Same logic can obviously be done using the indexes, but that might result in longer code. Might have to update some of the code if everything has to be fully immutable.

Upvotes: 0

Related Questions