0

Here is simplified example of my code

 constructor(props) {
        super(props);
        this.state = {
            components:[ComponentA, ComponentA, ComponentA, ComponentA, ComponentA ]
        }
    }

 hide(i){
        let components= this.state.components.filter((item,k)=>k!=i);
        this.setState({components});
    }


 render() {
       return (<div>
            {this.state.components.map((item,i) => {
                    let ChildComponent = item;
                    return <div key={i} onClick={()=>this.hide(i)}>
                              <ChildComponent />
                           </div>
             })

 }

Here I have instances of the same component. When I click on one - I expect the div with this particular instance to be removed. To check that - here is my ComponentA code:

constructor(props) {
        super(props);
        this.state = {
             uid: Math.random()
        }
    }
    render() {
        return (
            <div>
                ComponentA - {this.state.uid}
            </div>
        );
    }

So each instance have its unique uid in state. When I click on one of the the components - always the last is one being removed.

enter image description here


UPDATE

That was simplified version, It's working now. My real problem was with react-grid-layout:

this.state = {
            testlayout: [
                {"w": 10,"h": 100,"i": 0,"x": 0, "y": 0,"widget": Component4},
                {"w": 10,"h": 100,"i":1,"x": 10, "y": 0,"widget": Component4},
                {"w": 10,"h": 100,"i": 2,"x": 20, "y": 0,"widget": Component4},
            ]
        }

onClose(i){
        let testlayout = this.state.testlayout.filter((item,k)=>item.i!=i);
        this.setState({testlayout});
    }

render() {
            return (<div>
                    <ReactGridLayout>
                        {this.state.testlayout.map((item, i) => {
                                let ChildComponent = item.widget;
                                return 
                                <div onClick={() => this.onClose(item.i)}  data-grid={this.state.testlayout[i]} key={item.i}>
                                    <ChildComponent/>
                                </div>
                            }
                        )}

                    </ReactGridLayout>
                </div>
            );
        }

enter image description here

Anna
  • 2,911
  • 6
  • 29
  • 42

1 Answers1

3

well, this line of code, removes the last element, as the indexes will not be kept.

let components= this.state.components.filter((item,k)=>k!=i);

first of, look into the article about Reconcilation. It helps you to understand, why unique, predictable and stable keys are important.

Luke
  • 8,235
  • 3
  • 22
  • 36
  • Basically the gist of it is, this is why you shouldn't use array indices as keys in React. Keys are used to uniquely identify components. So when you remove one on click, the `.map()` creates another array of components, except one component less. However to React, all it notices is that there's still a bunch of divs with keys `0 to n-1`, with the "last one" missing, so it removes that from the DOM. It doesn't know that the one that was actually removed is somewhere other than the end, cos the keys are just the array index. So always use unique key for arrays of components. – Jayce444 Jun 04 '18 at 14:55
  • I changed this.state = {components:[{w:Component4,i:0},{w:Component4,i:1},{w:Component4,i:2},{w:Component4,i:3},{w:Component4,i:4}]} and remove by i – Anna Jun 04 '18 at 14:58
  • 1
    yes, that way it could work. as long as you also use that `i` as key. – Luke Jun 04 '18 at 15:01