Home > Back-end >  Is there a way to use data which is based on other data removed from an array (ReactJS)?
Is there a way to use data which is based on other data removed from an array (ReactJS)?

Time:04-11

I am working on a system for creating folders. The data is stored in an array like this and sorted by a pos (position) property:

const [ folders, setFolders ] = useState([
    {
      id: 5253 ,
      pos: 1 ,
      name: 'TEST_1',
    },
    {
      id: 586 ,
      pos: 2 ,
      name: 'TEST_2',
    },
    {
      id: 12331 ,
      pos: 3 ,
      name: 'TEST_3',
    },
])
folders.sort(function(a, b){return a.pos - b.pos})

When deleting a folder the pos property of every folder with a higher pos the property should be subtracted by 1 that no undefined "holes" (between two pos) are created (which would mess up the folder positions when for instance a new folder is created). For deleting a folder and adjusting the other positions the following code is used:

const deleteFolder = (id, pos) => {
    setFolders(folders.map((folder) => folder.pos > pos
    ? { ...folder, pos: (folder.pos - 1) } : folder ))

    setFolders(folders.filter((folder) => folder.id !== id))
}

The first part of the code (folders.map) is for adjusting the positions, the second part (folders.filter) is for the actual deletion. However, the changed pos properties are switching back to their original value when the setFolders(folders.filter((folder) => folder.id !== id)) code (for deleting the folder) runs (if you remove that line of code the position adjustment works fine).

I am assuming that after the folders.filter the used pos is no longer available and the issue occurs for that reason. Is there any way to avoid this problem?

CodePudding user response:

The useState hooks update the value asynchronously. According to the react documentation:

if the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.

So yes you're right, the filter update might occur before the map update and mess with the pos index modification. There is an example on the documentation showing the use of a functional update.

CodePudding user response:

You should perform both map and filter on the same setState:

    const deleteFolder = (id, pos) => {
    setFolders((folders) =>
      folders
        .map((folder) =>
          folder.pos > pos ? { ...folder, pos: folder.pos - 1 } : folder
        )
        .filter((folder) => folder.id !== id)
    );
  };

Working example: https://stackblitz.com/edit/react-xso4qr

In your case it doesn't work before setStates are batched so you are basically overriding the first one by calling the second one since your are filtering the folders value at the moment you call deleteFolder and not the updated mapped array. If you want to execute different setState in sequence when batched, you need to use a callback, like setState( currentState => newState ), in this way, even if you call several setState in sequence they will work properly.

CodePudding user response:

You should use the previous value of the state in the setFolders.

setFolders((previousFolders)=>{
   return(previousFolders.filter((folder) => folder.id !== id))
})
  • Related