Always delete the last dropdown in the screen but the right index on the table

I have as many dropdown menus as the values in a table and when I remove a value from table the state it is update correctly. For example state is [“1″,”2″,”3″,”4″,”5”] and i click on button X on value 3 state will be [“1″,”2″,”4″,”5”] but on screen the last dropdown will be deleted with wrong value as shown in the pic.

 {this.state.knowledge.map((product,index) => (
                <Grid
                  item
                  key={index+1}
                  lg={4}
                  xl={4}
                  sm={12}
                  md={6}
                  xs={12}
                  style={{display: 'inline-flex', alignItems: 'center', margin: '10px 0'}}
                >
                <span style={{color: '#263238',fontFamily: 'Roboto,Helvetica,Arial,sans-serif', fontSize: '18px', fontWeight: '400', lineHeight: '1.334', letterSpacing: '-0.05px', marginRight: '10px'}}>{index+1})</span> 
                <Autocomplete
                id="combo-box-demo"
                options={options}
                getOptionLabel={(option) => option}
                style={{ width: 300 ,marginRight: '2px'}}
                renderInput={(params) => <TextField {...params} label="Select knowledge" variant="outlined" />}
                />
                 <IconButton
                aria-label="account of current user"
                aria-controls="menu-appbar"
                aria-haspopup="true"
                onClick={() => this.handleDeleteKnowledge(index)}
                color="inherit"
                style={{backgroundColor:"transparent"}}
              ><CloseIcon style={{marginRight: '25px', color: '#3f51b5', fontSize:'20px'}} /></IconButton>
            
                </Grid>
  handleDeleteKnowledge = (value) => {
    this.setState({
      knowledge: this.state.knowledge.filter((x,i) => i !== value )
    });
  } 
  constructor(props) {
    super(props);
    this.state = {
      knowledge:["1","2","3","4","5"]
    };
  }

Screenshot examplee !! See example here

Answer

Issue

The issue here is that you are using the array index as the React key and mutating the underlying data, i.e. removing an element. When you remove an element from the array all the following elements shift up to fill the “hole” and yet the index that was used isn’t deleted. The diff that react sees is that the last React key is no longer there and so the last element is removed from the rendered output.

Solution

Don’t use the array index as a React key if you plan on mutating the array being mapped. If you can, use an intrinsic property of the elements being mapped, like an id property.

If the data doesn’t have some unique property to identify it you should provide your own. I suggest using uuid to provide a GUID for each data item, but any id generator that guarantees non-duplicate id values will work.

import { v4 as uuidV4 } from 'uuid';

...

constructor(props) {
  super(props);
  this.state = {
    knowledge: ["1","2","3","4","5"].map(item => ({
      id: uuidV4();
      item,
    }))
  };
}

Update the delete handler to consume and filter by an id.

handleDeleteKnowledge = (value) => {
  this.setState(prevState => ({
    knowledge: prevState.knowledge.filter((x) => x.id !== value )
  }));
} 

Update the UI to map and use the id property.

{this.state.knowledge.map((product, index) => (
  <Grid
    item
    key={product.id}
    ...
  >
    ...
    <IconButton
      ...
      onClick={() => this.handleDeleteKnowledge(product.id)} // <-- pass id
      ...
    >
      ...
    </IconButton>
  </Grid>

Leave a Reply

Your email address will not be published. Required fields are marked *