0

I am trying to change the state of my app without overriding the values that haven't changed. I am doing so using the spread operator, but something is going wrong.

Here is my base state : useState({data : [{name : undefined , project : []}] });

With setState i want to just add names but keep the project array empty since it didn't change.

setManager(prevState => ({...prevState , data : res.data}))

After performing setState the new state looks like this :

[
    {name: "John doe"},
    {name: "Jane Doe"}
]

as you can see the default state is completely overridden.

res.data looks like this by the way :

[
  {name: "john doe"},
  {name: "jane doe"}
] 
keikai
  • 14,085
  • 9
  • 49
  • 68
Kevin.a
  • 4,094
  • 8
  • 46
  • 82
  • Spread does a shallow merge. See [How to deep merge instead of shallow merge?](https://stackoverflow.com/q/27936772/215552) – Heretic Monkey Mar 30 '20 at 12:44

3 Answers3

1

By setManager(prevState => ({...prevState , data : res.data})) you're simply overriding your earier 'main' data property.

data is an array, new values are in array ... simply concat them

setManager(prevState => ({...prevState , 
  data : prevState.data.concat( res.data )
}))

After that you should have

[  
  {name: undefined , project : []},  
  {name: "john doe"},
  {name: "jane doe"}
] 

... but probably you wanted to manage names and project separately:

const [manager, setManager] = useState({
  data: { 
    name: undefined, 
    project: []
  }
});

... or even

const [manager, setManager] = useState({
  name: undefined, 
  project: []
});

This way you don't need to 'address' this data with 'intermediatory' .data

<Component names={manager.name} />

... not by {manager.data.name}.

Of course updating only name needs different code

setManager(prevState => ({
  ...prevState, 
  name : prevState.name.concat( res.data )
}))
  • ...prevState prevents deleting project property
  • name should be initialized with [], not undefined
  • conditional rendering can be done with `{!manager.name.lenght && ... }

If you have separate states 'slices' then no need to use one common state, use many useState declarations.

xadm
  • 8,219
  • 3
  • 14
  • 25
1

Probably what you need is:

const [manager, setManager] = useState({
  data: [{ name: undefined, project: [] }]
});

setManager(prevState => ({
  data: prevState.data.map((item, index) => ({ ...item, ...res.data[index] }))
}));

However, if you're just storing an array of "items", then your state should look more like:

const [manager, setManager] = useState([{ name: undefined, project: [] }]);

setManager(prevState =>
  prevState.data.map((item, index) => ({ ...item, ...res.data[index] }))
);

Also, the way how you're gonna merge prev and incoming data depends on many things and maybe you need some more sophisticated way of storing state.

Łukasz Jagodziński
  • 3,009
  • 2
  • 25
  • 33
1

You inited your state with an Array, not an Object, that's the reason for the behavior.

Change this from

useState({data : [{name : undefined , project : []}] });

to

useState({data : {name : undefined , project : []} });
keikai
  • 14,085
  • 9
  • 49
  • 68