What if there is no opportunity to create unique keys?

88 Views Asked by At

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

There are 2 best solutions below

0
On BEST ANSWER

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.

4
On

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>
  )
}