2

I have a complex data set, so I will show a very simplified version for an example.

Input data:

const data = [
  {
    type: "input",
    caption: "Name",
    defaultValue: "John Smith"
  },
  {
    type: "input",
    caption: "Name",
    defaultValue: "John Smith"
  },
  {
    type: "input",
    caption: "Name",
    defaultValue: "John Smith"
  },
  {
    type: "input",
    caption: "Name",
    defaultValue: "John Smith"
  },
  {
    type: "input",
    caption: "Name",
    defaultValue: "John Smith"
  }
];

Each item of the array is removable. It turns out something like this.

enter image description here

There are several conditions. I should not modify the data array so i create a deep copy. As well inside the copy i can only delete elements but don't modify their properties. Thus each element has to have local state with a new value.

Working example:

function App() {
  const [mainData, setMainData] = useState(deepCopy(data));

  return (
    <React.Fragment>
      {
        mainData.map((item, i) => {
          return (
            <Input {...item} key={i} num={i} setMainData={setMainData}/>
          )
        })
      }
    </React.Fragment>
  )
}

const Input = (props) => {
  const [value, setValue] = useState(props.defaultValue);

  const deleteElem = () => {
    props.setMainData((mainData) => {
      return [...mainData.filter((_, ind) => ind !== props.num)];
    });
  };

  return (
    <div>
      <div>
        <div>{`${props.caption}:`}</div>
        <input value={value} onChange={(e)=>setValue(e.target.value)}/>
      </div>
      <button onClick={deleteElem}>delete</button>
    </div>
  )
};



const deepCopy = (aObject) => {
  if (!aObject) {
    return aObject;
  }
  let value;
  let bObject = Array.isArray(aObject) ? [] : {};

  for (const key in aObject) {
    value = aObject[key];
    bObject[key] = (typeof value === "object") ? deepCopy(value) : value;
  }

  return bObject;
};

If you try to delete not the last element then (because of the keys) the values of the inputs elements will be mixed up. What can I do about it?

2 Answers2

3

With deepCopy you can add a unique id to each item when you initialize your state. Once you do that you can leverage that id for passing as key to the Input element

import {uuid} from 'uuidv4';
function deepCopyAndAddId = () => {
   let newData = deepCopy(data);
   newData = newData.map((item, index) => ({...item, id: uuid()})); 
}
function App() {
  const [mainData, setMainData] = useState(deepCopyAndAddId);

  return (
    <React.Fragment>
      {
        mainData.map((item, i) => {
          return (
            <Input {...item} key={item.id} num={i} setMainData={setMainData}/>
          )
        })
      }
    </React.Fragment>
  )
}
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • @giorgim Yes exactly, but while creating id you can initially use index since its once time and will not change. As I also commented, you can use uuid package to generate ids. Anyways I updated the answer to a more correct approach – Shubham Khatri May 22 '20 at 10:12
  • @giorgim you are mistaken, Please look when the ids are created, its only on setting initial state. They don't change afterwards – Shubham Khatri May 22 '20 at 10:16
  • Ohh dear, how do I explain you this. Key is not passed as index but as id and id doesn't change between renders – Shubham Khatri May 22 '20 at 10:20
  • Yes, using index is not good that is. why the person was facing an issue. We should associate unique ids with each object. The thing is key must not change between re-renders – Shubham Khatri May 22 '20 at 10:24
1

To make minimum changes in your code - just never delete the item in deleteElem, but add a flag deleted to it instead.

When render an item, show <Fragment> for the deleted item.

Eugene Tiurin
  • 4,019
  • 4
  • 33
  • 32