0

In my React app I purposely combined several arrays in one state using a useState hook. These arrays represent visual objects that I would like to keep "managed" together to ensure that that re-renderings of my application are visually consistent.

While testing I tried to change a property of some objects of my first array. updatedElements reflects the update properly (clearly shown by my console-log). However, updating my useState state does not work. The array elements does not change at all.

Here is the relevant code:

const updatedElements: VisualDiagramElementData[] =
  visualData.elements.map((element: VisualDiagramElementData) =>
    element.id === id
      ? { ...element, selected: true }
      : { ...element, selected: false }
  );

console.log(updatedElements);

setVisualData({
  elements: updatedElements,
  connectors: visualData.connections,
  connections: visualData.connections,
});

What am I missing / doing wrong? Any help is highly appreciated!

bassman21
  • 320
  • 2
  • 11
  • 1
    Can you please add more code snippets please? Like how are you updating state like in which function and how are you changing elements of your object? – akshay2739 Aug 25 '22 at 04:45
  • Please help to understand better, what you would like to see. Because the code I posted shows both: I create a new array `updatedElements` by changing the property "selected" of some "elements" to either true or false. Then I update my state with `setVisualData` by using the newly created array `updatedElements` as argument. – bassman21 Aug 25 '22 at 04:47
  • Add how `updatedElements` will be changed, Like is it used in `onChange` or `onSubmit` events? – akshay2739 Aug 25 '22 at 04:49
  • `updatedElements` will not be changed any more. I create this array. And I try to set my array `elements` "within my state" with it. Both is shown in my code. – bassman21 Aug 25 '22 at 04:52

3 Answers3

0

When updating a state property based on its previous value, the callback argument of the state setter should be used. Otherwise you may use stale state values.

https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
https://reactjs.org/docs/hooks-reference.html#functional-updates

It should be:

setVisualData((visualData)=>({
    elements: visualData.elements.map(/*Your map callback*/),
    connectors: visualData.connections,
    connections: visualData.connections,
}));
Nice Books
  • 1,675
  • 2
  • 17
  • 21
  • Thank you very much for hint! I was already assuming that it could by a "stale state issue". I implemented your suggestion but, unfortunetaly, it still does not work. I even removed the ternary operator just to make sure that I actually set property `selected` from false to true. `visualData` still does not change. – bassman21 Aug 25 '22 at 05:02
  • The issue could be elsewhere, but please keep the callback argument, as the stale value issue is quite common. – Nice Books Aug 25 '22 at 05:03
0

I would go with what is recommended in the React docs - split them out into multiple state variables: https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables.

Depending on how you trigger the state updates will determine whether re-render occurs multiple times or not. See this answer for more details on how the batching works: https://stackoverflow.com/a/53048903/6854709

Also take note of the comment by aprillion:

Note from github.com/facebook/react/issues/10231#issuecomment-316644950 - This is implementation detail and may change in future versions.

sneaky squid
  • 128
  • 7
0

Eventually, it worked by using the "callback argument of the state setter". I revised / reviewed my entire code and I modified all my state setters accordingly. Now, everything works like a charm. Thank you very much again for your help, especially to Nice Books.

bassman21
  • 320
  • 2
  • 11