React Redux - Updating a nested object changes state structure

Right now I am working a bit with Redux and so far it’s cool, but there’s an issue. I want to update something that is nested. And with the solution so far it makes an update, but in a way that it is not good for the app.

Let me show you two images.
Screen Shot 2022-05-30 at 12.44.43
Screen Shot 2022-05-30 at 21.59.32

This is my redux state before an update.
Screen Shot 2022-05-30 at 12.47.53
This is the redux state after the update.

What I’m doing is the following:

case ActionTypes.UPDATE_CV_PROF:
    //return { cv: {...state.user ,professional :payload }};
    return {
      ...state,
      cv: {
        ...state.cv,
        professional: {
          ...state.cv.professional,
          1: {
            ...state.cv.professional[0],
            position: "Hello"      
          }
        }
}};

So, instead of causing these changes, I’d like to just change the position’s value. But how? I’ve been playing around with the index number and all, but all failed.

What can I do to update the store without changing the structure as I currently do by accident?

You return an object. But you want to update an array instead. So just change the return statement to this:

return [
...state,

Hi mirja_t,
Thanks for your reply. Sounds reasonable so far. However, doing this let errors come up, like expression missing or syntax errors.

I quoted the wrong line of your code, but the cause of the error is the same: professional is an array, but you return it as an object.
So you have a state, that state is an object. It contains properties keyed by ‘professional’ and ‘school’. Both properties contain arrays, so you need to return them as such.

return {
      ...state,
      cv: {
        ...state.cv,
        professional: [
          ...state.cv.professional,
          {
            ...state.cv.professional[0],
            position: "Hello"      
          }
        ]
      }
    }

This way you (probably – I don’t know all of your code) don’t get a syntax error and ‘professional’ stays an array. But I doubt that it returns what you want as it adds an additional professional with a copy of the first professional, added by a new position.

1 Like

Thanks, mirja, you are right, it’s not the expected output, but still useful. I’d try to figure out how to fix it by tomorrow, or else I’ll reply to this post by tomorrow.

1 Like

Okay, I got it.

state.cv.professional[0]["position"] = "Placeholder";

But I am not sure if this is right. The value in Redux DevTools has changed, but it looks different from the code usually shown for updates.
How would you have done in, mirja?

I don’t really understand what your asking tbh. Could you provide a code snippet of how you want your state to look after the update?

Of course.
Following is a raw from the Redux DevTools panel.

{
  professional: [
    {
      user_cv_id: 59,
      user_id: 1,
      entity_id: 10447,
      category: 0,
      position_category: 0,
      position: 'Johnny',
      city: 'Sheridan',
      state: 'WY',
      country: 'US',
      start_date: '2020-10-31T00:00:00.000Z',
      end_date: null,
      name: 'Company Name',
      url: 'url.com',
      urlname: 'urlcom',
      division_name: 'Wyoming',
      country_name: 'United States'
    },
    {
      user_cv_id: 97,
      user_id: 1,
      entity_id: 10447,
      category: 0,
      position_category: 0,
      position: 'Back-End Developer',
      city: '',
      state: 'WY',
      country: 'US',
      start_date: '2021-01-31T00:00:00.000Z',
      end_date: null,
      name: 'Company Name',
      url: 'url.com',
      urlname: 'urlcom',
      division_name: 'Wyoming',
      country_name: 'United States'
    },
    {...

and after an update for the first element, updating position to “Redux Developer” and name to “Web name” the following

  professional: [
    {
      user_cv_id: 59,
      user_id: 1,
      entity_id: 10447,
      category: 0,
      position_category: 0,
      position: 'Redux Developer',
      city: 'Sheridan',
      state: 'WY',
      country: 'US',
      start_date: '2020-10-31T00:00:00.000Z',
      end_date: null,
      name: 'Web name',
      url: 'url.com',
      urlname: 'urlcom',
      division_name: 'Wyoming',
      country_name: 'United States'
    },
    {
      user_cv_id: 97,
      user_id: 1,
      entity_id: 10447,
      category: 0,
      position_category: 0,
      position: 'Back-End Developer',
      city: '',
      state: 'WY',
      country: 'US',
      start_date: '2021-01-31T00:00:00.000Z',
      end_date: null,
      name: 'Company Name',
      url: 'url.com',
      urlname: 'urlcom',
      division_name: 'Wyoming',
      country_name: 'United States'
    },
    {...

That’s what I meant. Sorry if it wasn’t clear.

Hmm, I don’t see the difference of the two code blocks, positions are the same…
But you want to update just one property of an existing object in 'professional, right? Always the first object or by what criteria?

My fault. I thought I have edited it correctly. Now it’s right.

Ok, I see. Still my question:

1 Like

Ah, overseen. No, this is a variable number that I’d get from the payload.

What is in your payload? The id of the object you want to update, the name of the key of the property, you want to update and the value (in this case position) you want to update? Otherwise you won’t be able to find the object and property you want to update. You would just be able to add a new object as you do now.

The payload would include the key and the values to update. Key reflects the index of each element in the redux state.

So the key is the index of the object in the array? And how do you tell the code which property (here: position) to update? Or will your action always update the position, never the country_name, for example?

All properties would be transported. I made it that simple to better understand how to solve the issue, taking complexity out of the issue.

Ok, then to understand the structure of your payload, is that what it looks like:

{
  key: 0, // index of object in the array you want to update
  data: {
    user_cv_id: 59,
    user_id: 1,
...
  }
}

?

1 Like

Exactly the right structure.

Ok, then if you have the whole object in your payload, why don’t you just replace the whole object?

The payload wouldn’t tell that only ‘position’ should be updated. No need for this, then:

You could rather find the object in question:

state.cv.professional[action.payload.key]

and completely replace it by the data in your payload.

Many, many thanks. I now got to the point where it is working and your answer gave the right direction. Here’s how I solved it:
for (const x in payload) {

      if(x!='key'){
        state.cv.professional[payload["key"]][x] = payload[x];            
      }

    }

I still need to make some adjustments, but the redux dev tools already show the right results.
Thanks, Mirja.

1 Like