'Weird return of data after splice and setstate

Here's simulate in codesandbox

https://codesandbox.io/embed/epic-nash-mxteu?fontsize=14&hidenavigation=1&theme=dark

I am having a weird behavior when I remove a row from the dynamic rows created.

I have removed the row with 2 b. As you can see the console log has the correct data while the UI showing wrong data. It means the function works well but somehow displaying incorrectly.

Anyone have idea why? Thanks in advance

Screenshots

Before remove row

enter image description here

After remove row

enter image description here

Source code

const [gavRows, setGAVRows] = useState([]);

const handleGAVAddRow = async () => {
    try {
        const item = {
            gav_field: '',
            gav_value: ''
        };
        setGAVRows([...gavRows, item]);
    } catch (error) {
        console.log('error', error)
    }
};

const handleGAVRemoveSpecificRow = (idx) => {
    console.log('idx', idx)
    const tempRows = [...gavRows];
    console.log('tempRows', tempRows)
    tempRows.splice(idx, 1);
    setGAVRows(tempRows)
};


const handleGAVChange = async (idx, e) => {
    const { name, value } = e.target;
    var tempRows = [...gavRows];
    tempRows[idx][name] = value;

    setGAVRows(tempRows)
};


<table className="customgav_section">
    <tbody>
        {
            gavRows.map((item, idx) => {
                console.log('map idx', idx, item)
                return (
                    <tr key={idx}>
                        <td>
                            <Input type="text"
                                name="gav_field" id="gav_field"
                                value={gavRows[idx].field}
                                onChange={(e) => handleGAVChange(idx, e)}
                            />
                        </td>
                        <td>
                            <Input type="text"
                                name="gav_value" id="gav_value"
                                value={gavRows[idx].value}
                                onChange={(e) => handleGAVChange(idx, e)}
                            />
                        </td>
                        <td>
                            <Button outline color="danger" onClick={() => handleGAVRemoveSpecificRow(idx)}><FaMinus /></Button>
                        </td>
                    </tr>)
            })
        }
    </tbody>
</table>


Solution 1:[1]

Your problem is that you are using the index of the array as the key.

Read why that is bad: https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318

A quick hack was assigning a random number as the key of each item in the gavRows and using that as the key of the element. See updated code: https://codesandbox.io/s/charming-bouman-03zn7

Also, the id of an element must be unique in the DOM so i removed those from the input elements.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Gabriele Petrioli