Displayed data does not update in nested loop on state

Viewed 35

I have an object looking like this:

categories : [
    { 
        name: "General Information",
        rows: [
            {
                information1 : "",
                information2 : "",
            }
        ]
    }
]

And I am displaying the rows content using a loop like this:

{formData?.categories?.map(c => (
    <div>
        {c.name}
        {c.rows.map((row) => (
        <div>
            <input value={field.information1} />
            <textarea value={field.information2}></textarea>
            <button onClick={() => addRow(c.name)}>Add Row here</button>
        </div>
    ))}
    </div>

))}

When I press one of these buttons (that should add a row to rows array) the formData state does update, but the displayed data does not.

function addRow(category) {

    const updatedFormData = formData

    for (const cat of updatedFormData.categories) {
        if (cat.name === category) {
            cat.rows.push({
                information1 : "test",
                information2 : "this is a test"
            })
        } 
    }

    setFormData(updatedFormData)
} 
1 Answers

In the addRow function, create a copy of formData before updating it

function addRow(category) {

    const updatedFormData = {...formData};

    // Make changes to updatedFormData

    setFormData(updatedFormData)
}

We need to create a copy before updating to let react know that the state has changed.

Consider this scenario,

const obj1 = {a : 1, b: 2};
const obj2 = obj1;
obj2.b = 12;

console.log(obj1 === obj2); // true

obj1 and obj2 point to the same object reference. Even if we changed the value of b, the reference has not changed. Similarly, when we updated the categories of formData without creating a copy, the reference had not changed, and react has no way to know that the state has been updated. So we create a new object and copy the contents of formData to it and then update the copy and set the state with the copy.

From the official docs:

State can hold any kind of JavaScript value, including objects. But you shouldn’t change objects that you hold in the React state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.

Related