Eitan
Eitan

Reputation: 1494

How to update nested properties without overriding in an immutable Javascript function

I want to know how I can return an immutable object from a function (a reducer in react but that's not really important) to combine an array of objects (state) with another object payload that has a nested object subResults. I basically want to return state with the 2 records below but the first record would have a sub property called subNodes because state.results[0].name === payload[0].name

  state = {results:[
    {name:'name 1', description: 'I am a description'},
    {name:'name 2', description: 'I am a second description'}
    ]};



  payload = {results: [ {name:'name 1', subResults:{type:'whatever'} }]};

It kinda works if I call:

{
   ...state,
   results: {
   ...state.results,
   ...payload.results[0]
   }
}

The problem here is that I lose the 'description' property because the payload object doesn't have 'description' and overrides the state object. I want to keep the state object as it is and only append the 'subResults'. I'm also hard coding the index[0] while I would like to use the name property to override like I mentioned above.

Anyone have any ideas?

Upvotes: 1

Views: 271

Answers (1)

Manuel Cheveste
Manuel Cheveste

Reputation: 11

Result is an array of object, so you would have to iterate over the values, also if you want to merge the value if the name are the same, a couple of array functions could be helpful

const state = {
  results: [
    { name: 'name 1', description: 'I am a description' },
    { name: 'name 2', description: 'I am a second description' },
  ],
}

const payload = {
  results: [
    { name: 'name 1', subResults: { type: 'whatever' } },
    { name: 'name 3', description: 'third one' },
  ],
}

function merge(state, payload) {
  return {
    ...state,
    ...payload,
    results: payload.results.reduce((newResults, newItem) => {
      // if item doesn't exist on the state, we add it
      if (!newResults.some(item => newItem.name === item.name)) {
        return [...newResults, newItem]
      }

      // otherwise, we merge it with the payload item information
      return newResults.map(item => {
        if (item.name === newItem.name) {
          return {
            ...item,
            ...newItem,
          }
        }

        return item
      })
    }, state.results),
  }
}

const newState = merge(state, payload)

console.log(newState)

Upvotes: 1

Related Questions