Can't remove specific elements from array by index number from dynamic div element in react?

Viewed 273

I can't remove specific elements of array by index number from any dynamic div

const { useState } = React;

function Check(){
  var [Children, setChildren] = useState([])
    
  function RemArr(docs){
    const temp = [...Children]
    temp.splice(docs["i"], 1);
    setChildren(temp)
  } 
    
  function Product(docs) {
    var document = (<React.Fragment>
      <button onClick={()=>RemArr(docs)}>
        List {docs["i"]}
      </button>
      <p></p>
      <input/>
      <p></p>
    </React.Fragment>);
    setChildren([...Children, document])
  }

  return (<React.Fragment>
    <div>Check</div>
    {Children}
    <button
     onClick={()=>{Product({fix:false, i:Children.length})}}
    >Add List </button>
  </React.Fragment>)
}

ReactDOM.render(<Check />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>

When i want to remove any specific div then not only this div remove from array, also remove next div from array.

Eg.

array = [0,1,2,3,4,5,6,7,8,9]

If I want to remove only 6 from the array, however, it instead removes all the values from 6 to 9, and so I am left with just:

array = [0,1,2,3,4,5]

I think the problem originates from this function:

function RemArr(docs){
  const temp = [...Children]
  temp.splice(docs["i"], 1);
  setChildren(temp)
}
1 Answers

Your RemArr function has a closure over Children, meaning that clicking on the remove button will call a potentially old declaration of remArr, where inside that remArr, the Children array will refer back to the Children array state from when you originally added the click event handler, rather than the current state of Children.

To overcome this, you can componentize Product rather than making it a function, and drive the Children based of some state in Choice, which you can use to generate your Product elements:

const {useState} = React;

function Product({docs, onClick}){
  return <React.Fragment>
    <button onClick={onClick}>
      List {docs.i}
    </button>
    <p></p>
    <input/>
    <p></p>
  </React.Fragment>;
}

function Check() {
  const [products, setProducts] = useState([]);
  function remArr(index){
    const temp = [...products];
    temp.splice(index, 1);
    setProducts(temp);
  }
  
  function addProduct() {
    const prevNum = products[products.length-1];
    setProducts([...products, {
      fix: false, 
      i: prevNum === undefined ? 0 : prevNum.i + 1
    }]);
  }

  return (<React.Fragment>
    <div>Check</div>
    {products.map(
      (doc, i) => <Product key={doc.i} docs={doc} onClick={() => remArr(i)}/>
    )}
    <button
     onClick={addProduct}
    >Add List</button>
  </React.Fragment>);
}

ReactDOM.render(<Check />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>

The above .map() method that creates new <Product /> components "updates"/re-sets the onClick function of all the product components to point to the "latest" declared remArr function, which has access to the correct/up to date products array. This is unlike your example, where the old JSX objects inside of the Children array still have onClick attributes that refer to the old declarations of the remArr function (which have access to the old Children array state)

Related