Home > Back-end >  Keeping index of array item in React constant after filtering
Keeping index of array item in React constant after filtering

Time:10-18

I'm building a React component, which currently displays a list of technologies. The user can toggle which of these technologies are 'active' (i.e. selected) by clicking on them. They start as false, change to true, back to false and so on.

When the user does this, they get added to the overall technologies array and displayed on the frontend.

This is done through a 'toggleTechnology' function, which gets two properties passed in to it: item (the technology text) and index (where this item is in the array).

 const toggleTechnology = (item, index) => {

        console.log(item)
        console.log(index)

        if (technologies.includes(item)) {
            let filteredArray = technologies.filter((currentItem, currentIndex) => currentIndex !== index)

            setTechnologies(filteredArray)
        } else {
            setTechnologies(tech => [...tech, item])
        }

    }

The list of technologies is kept in a separate array, to be displayed dynamically on the frontend, like so:

const frontEndSklls = ['HTML', 'CSS', 'Javascript', 'React', 'Unit tests', 'Vue.js']

{ frontEndSklls.map((item, index) => {
                                    return <span className={technologies.includes(item) ? "toggle-option active" : "toggle-option"} onClick={() => toggleTechnology(item, index)} key={index}>{item}</span>
                                }) }

My issue comes in when a user adds multiple skills and then removes some of them.

So for example, if they clicked on the following in this order, each item in the technologies array would have an index like this:

  • 0: HTML
  • 1: Unit tests
  • 2: React
  • 3: CSS

However, if they then clicked on 'React' to remove it, the array would go like this:

  • 0: HTML
  • 1: Unit tests
  • 2: CSS

If they then click on CSS to remove it, I think because the new index of CSS is 2, it isn't removing the property. So what you need to do is click on another element first, say React again, and then CSS can be removed as the new array has CSS at index point 2.

So my question: is there a way for me to adjust my code so that the index remains the same for each of the options, even when you click on and off the elements?

The solution to me seems to be to turn each element in the array into an object, and assign a permanent index number and value to each element in the array - but this doesn't feel as clean as it could be.

Any suggestions would be appreciated.

CodePudding user response:

You have two different arrays in the component:

  1. frontEndSkills - static - used to render all technologies in the UI with activated/deactivated status.
  2. technologies - dynamic - the filtered array used to mark the active technologies in the UI as such.

The problem is that you're supplying the index of an item from the frontEndSkills array to be used in the technologies array, which is not always the same as the former.

@Subham Jain's answer also would work perfectly, but here is my shot at it, without the need for the index at all:

const toggleTechnology = (item) => {

  if (technologies.includes(item)) {
    let filteredArray = [...technologies].filter(currentItem => currentItem !== item)
    setTechnologies(filteredArray)
  } else {
    setTechnologies(tech => [...tech, item])
  }
}

Note: The suggestion to not use the index as a key from the other answers/comments here is sensible and highly recommended. Please consider that seriously. If you do have to use it for any reason at all, make sure that you understand why it is anti-pattern to do so.

CodePudding user response:

I would suggest that change 2 things.

Instead of using "index" for the "key" use "item".

Do not toggle the state based on the index. To achieve this you can modify the "toggletechnology" as below:-

const toggleTechnology = (item) => {
        if (technologies.includes(item)) {
            let indexOfItem = technologies.indexOf(item);
            let filteredArray = [...technologies].splice(indexOfItem,1);
            setTechnologies(filteredArray)
        } else {
            setTechnologies(tech => [...tech, item])
        }
    }
  • Related