13

I have the following object. This object gets assigned a new value when the user clicks on a button.

state = {
  title: '',
  id: '',
  imageId: '',
  boarding: {
    id: '',
    test: '',
    work: {
      title: '',
      id: ''
    }
  }
}

My updated object looks like:

state = {
  title: 'My img',
  id: '1234',
  imageId: '5678-232e',
  boarding: {
    id: '0980-erf2',
    title: 'hey there',
    work: {
      title: 'my work title',
      id: '456-rt3'
    }
  }
}

Now I want to update just work object inside state and keep everything the same. I was using Object.assign() when the object was not nested but confused for nesting.

Object.assign({}, state, { work: action.work });

My action.work has the entire work object but now I want to set that to boarding but this replaces everything that is in boarding which is not what I want.

fscore
  • 2,567
  • 7
  • 40
  • 74

6 Answers6

27

You should manually merge deep object properties, try following:

Object.assign({}, state, { 
  boarding: Object.assign({}, state.boarding, {
    work: action.work
  }
});

or with spread operator

{
  ...state,
  boarding: {
    ...state.boarding,
    work: action.work
  }
}
dhilt
  • 18,707
  • 8
  • 70
  • 85
  • could you please explain the role of spread operator in your example and how that is different from your first merge deep prop? – fscore Oct 16 '17 at 02:05
  • 1
    @fscore I've just found very nice [link](http://redux.js.org/docs/recipes/UsingObjectSpreadOperator.html) on this topic, I believe you would like it! – dhilt Oct 16 '17 at 02:08
  • @dhilt: the example with spread operator is a good one, but it should be assigned to `state` or to a constant or variable. The way how it's posted is causing an error and confusing it user would use it just as it is I would give the example such as: `state = { ...state, boarding: { ...state.boarding, work: action.work } }` – k.vincent Feb 12 '20 at 11:15
2

If merging them manually as @dhilt suggested is not an option, take a look at lodash's merge.

You can use mergeWith if you want to customise the merge behaviour, e.g. merge arrays instead of overriding.

felixfbecker
  • 2,273
  • 1
  • 19
  • 24
1

You can use Object.assign to update nested objects, for example:

Object.assign(state.boarding, { work: action.work })

This will update the state in place with the new work properties.

Cahil Foley
  • 192
  • 1
  • 3
  • 1
    This will NOT return `state`, but `state.boarding`. – Jan Kalfus Oct 16 '17 at 06:24
  • I didn't say it would return it, I said it would update state in place. – Cahil Foley Oct 18 '17 at 11:10
  • Actually, I was wrong. This doesn't even compile in most cases. And even when it does, it has zero effect. Please read the docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign I'm sorry, but you should delete this answer as it's completely wrong. I'm going to downvote it otherwise. – Jan Kalfus Oct 18 '17 at 14:52
1

> Use JavaScript spread operator:

{ ...user, staff_information: { nid: e.target.value }
0

If you just want to update work, you don't need any sort of merge. Just do

state.boarding.work = action.work;


Are you concerned with immutability, you can make a copy of state and then update work.

Ty Le
  • 8,494
  • 1
  • 11
  • 7
0

I can see you're using Redux. There's a great article in the docs about updating objects in an immutable way. I suggest you read it: http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html

Also, people usually use libraries for this, like Immutable.js or seamless-immutable, which don't make your code that disgusting to look at :)

Jan Kalfus
  • 5,422
  • 2
  • 30
  • 38