I followed the answer in this thread to try to update my deeply nested object in React. React: Setting State for Deeply Nested Objects w/ Hooks
What seems to work like a charm there, will somehow break for me when doing the following:
I have a table populated with items from an array defined like so:
const [items, setItems] = useState([
{
selected: false,
title: 'Item 1',
status: 'new'
},
{
selected: false,
title: 'Item 2',
status: 'used'
},
]);
When selecting an item from that list this function gets called to update selected variable for the object with the index i like so:
const select = (e) => {
const i = e.target.getAttribute('data-index');
setItems((prevState) => {
prevState[i].selected = !prevState[i].selected;
return [...prevState];
});
};
This will work exactly once. If I trigger select a second time or any time after that return [...prevState] somehow keeps returning the state unchanged. (selected stays true forever). I can't solve this.
items is attached to a component List like so:
<List
items={items}
/>
and inside List (shortened code):
{items.map((item, i) => {
return (
<tr className="list-table-tr">
{hasSelector ? (
<td className="list-table-td-selector">
{item.selected ? (
<div
data-index={i}
className="global-selector-selected"
onClick={select}
></div>
) : (
<div
data-index={i}
className="global-selector-unselected"
onClick={select}
></div>
)}
</td>
) : null}
You're breaking one of the primary rules of React state: You're modifying a state object directly, rather than making a copy.
To correctly do the update, you'd do this:
const select = (e) => {
const i = e.target.getAttribute('data-index');
setItems((prevState) => {
// Copy the array (your code was doing that)
const update = [...prevState];
const item = update[i];
// Copy the object (your code wasn't doing that) and update its
// `selected` property
update[i] = {...item, selected: !item.selected};
return update;
});
};
Note how both the array and the object are copied, rather than just the array.