Search code examples
reactjsmultidimensional-arrayreact-hooksfrontendweb-deployment

Deleting nested array element from a react state variable, doesn't update the state value in UI but has the updated value


I am new with react and working on a project where I am using the following

const [skillSet, updateSkillSet] = useState([{
        skillType : '',
        skillDescr : [
            {
                skillName : '',
                skillExp : ''
            }
        ]
    }])

when elements are added array looks like

[
    {
        "skillType": "fe",
        "skillDescr": [
            {
                "skillName": "s1",
                "skillExp": "3"
            },
            {
                "skillName": "s2",
                "skillExp": "3"
            },
            {
                "skillName": "s3",
                "skillExp": "2"
            }
        ]
    },
    {
        "skillType": "be",
        "skillDescr": [
            {
                "skillName": "b1",
                "skillExp": "2"
            },
            {
                "skillName": "b2",
                "skillExp": "2"
            },
            {
                "skillName": "b3",
                "skillExp": "2"
            }
        ]
    }
]

when I add an element to the skillDescr array it's working fine and it's rendering properly in the UI. But when I delete an element from skillDescr, the values that gets removed from the array is correct(the one which I am trying to delete) but in the UI the last element is being removed.

output(UI) after deleting s2,b2

array after deleting s2,b2

This is the code I am using to delete the element

function deleteSkill(e, index, index1){
    e.preventDefault()
    skillSet[index].skillDescr = skillSet[index].skillDescr.filter((_,i)=>i!=index1)
    updateSkillSet(skillSet)
}

I have index, index1 as there are two arrays I am using map to render the UI

here is the code

{
    skillSet.map(({skillType, skillDescr},index)=>{
        return(
            <div key={index} className='skills__box'>
                <div className='skill__type'>
                    <input placeholder='Skill set name' onChange={(e)=>{updateSkilltype(e,index)}}></input>
                    {
                        skillDescr.map(({skillName, skillExp},index1)=>{
                            return(
                                <div key={index1} className='skill__name'>
                                    <input placeholder='Skill name' onChange={(e)=>{updateSkillname(e, index, index1)}} required></input>
                                    <input  placeholder='Experiece Level' onChange={(e)=>{updateSkillexp(e, index, index1)}} type='number' required></input>
                                    <AiTwotoneDelete  onClick={(e)=>{deleteSkill(e, index, index1)}}/>
                                </div>
                            )
                        })
                    }
                    <button onClick={(e)=>{addSkill(e,index)}} className='btn btn-primary'>Add skill</button>
                </div>
            </div>
        )
    })
}

So my problem here is the array is getting updated properly but the render that's happening after the state update is not correct


Solution

  • That is because React doesn't know which element got removed. You need to add a key prop to all elements that correspond with the data rendered inside the loop so React can compare properly. Always try to prevent indices as key's.

    Using something unique and stable, like an ID, would be even better.

    {
        skillSet.map(({skillType, skillDescr},index)=>{
            return(
                <div key={skillType} className='skills__box'>
                    <div className='skill__type'>
                        <input placeholder='Skill set name' onChange={(e)=>{updateSkilltype(e,index)}}></input>
                        {
                            skillDescr.map(({skillName, skillExp},index1)=>{
                                return(
                                    <div key={skillName} className='skill__name'>
                                        <input placeholder='Skill name' onChange={(e)=>{updateSkillname(e, index, index1)}} required></input>
                                        <input  placeholder='Experiece Level' onChange={(e)=>{updateSkillexp(e, index, index1)}} type='number' required></input>
                                        <AiTwotoneDelete  onClick={(e)=>{deleteSkill(e, index, index1)}}/>
                                    </div>
                                )
                            })
                        }
                        <button onClick={(e)=>{addSkill(e,index)}} className='btn btn-primary'>Add skill</button>
                    </div>
                </div>
            )
        })
    }