Accepted answer

What your lead tech meant by "breath over depth" is right.

React monitors only the "reference" changes. When you change a property of an object, the reference is the same.

Think of it as when you do, you can still change a.value even though it's declared constant.

const a = {value: 'abc'}
a.value = 'xxx'
console.log(a.value) // prints 'xxx'

But you can't do

const a = {value: 'abc'}
a = {something: 'bad'}
👆 not allowed.

The same applies to an array of objects (or a plain old array). It's a reference type, thus changing the property of an array element would not change the reference of newArray.

newArray[2].text = '3' and setArray(newArray)

newArray still points to the same address space.

So when you create an entire new array using a spread syntax or Array.from, you create an entire new reference (a copy) and, set a new value, and lastly set the state.

const [array, setArray] = useState([
        {'id': 0, text: '0'},
        {'id': 1, text: '1'},
        {'id': 2, text: '2'}

// ... somewhere else.
const copy = [...array, {'id': 2, ]
// or -> const copy = Array.from(array)
copy[2].text = '3' 

The code above creates a new copy of array state. Setting that copy with updated value will trigger the re-render. (but beware, it creates a "shallow copy", not a "deep copy")

Since your data structure is complex, you might as well use ImmerJS, which lets you change the reference as if you are writing current code.


I think the problem is in how you are copying the array. Arrays in js are reference type so you can't just do this:

const arr = [1,2,4]
const copy = arr

Cause you are just changing the pointer not the reference itself. Try this:

const copy = [...arr]

Now you are creating a new array with the spread of every arr elements. To do that using useState

const Comp = () =>{
    const [arr, setArr] = useState([1,2,3,4])

    return <button onClick={() => setArr([...arr, 5])}>Change</button>

Related Query

More Query from same tag