Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing object property in an array with React Hooks does not evoke a re-render

When I change the property of an object in an array with react hooks, the component does not re-render.

I consulted my lead tech and he explained that React searches "breadth" over "depth," meaning React sees the object, but it can't see changes to the object's properties.

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

I expect this the component to re-render when the state is changed, but the behavior acts as if the array of objects were not changed at all.

If I create a copy of array as newArray and change newArray[2].text = '3' and setArray(newArray), react thinks the array hasn't changed. Thus, the component does not reload.

As a workaround, I use

const [trigger, setTrigger] = useState(false);
setTrigger(!trigger); 

to force reload. However, this is a hacky way to reload the component and I want a more React way of doing it.

Thank you advance!

like image 286
Michael Hayashi Avatar asked Jul 31 '19 18:07

Michael Hayashi


People also ask

Does changing props cause re-render React?

React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically.

Does useRef trigger re-render?

Conclusion. React's useRef hook is a great tool to persist data between renders without causing a rerender and to manipulate the DOM directly. It should only be used sparingly in situations where React doesn't provide a better alternative.

Does useEffect re-render?

Inside, useEffect compares the two objects, and since they have a different reference, it once again fetches the users and sets the new user object to the state. The state updates then triggers a re-render in the component.

What are react hooks?

In version 16.8, React hooks were introduced. Hooks allow a component to be built as a function without the need for classes. This component will need a state variable to track the background color and a ref to store the current timer instance.

Can react see changes to the properties of an object?

I consulted my lead tech and he explained that React searches "breadth" over "depth," meaning React sees the object, but it can't see changes to the object's properties. const [array, setArray] = useState ( [ {'id': 0, text: '0'}, {'id': 1, text: '1'}, {'id': 2, text: '2'} ]);

Why doesn't React React React detect changes in state of comments?

It looks like you're rendering off of the 'sale' state property. React will not detect an updated state and re-render when there is a change within a nested state object (in this case the 'comments' property).

Do state hooks re-render if you pass the same object?

exactly as @malerba118 said, state hooks will not rerender if you pass the same value/object to it. React encourages an immutable approach to ensure consistency, especially between renders.


2 Answers

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' 
setArray(copy)

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.

like image 86
dance2die Avatar answered Sep 22 '22 08:09

dance2die


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>
}
like image 36
Dupocas Avatar answered Sep 21 '22 08:09

Dupocas