Jobz
Jobz

Reputation: 462

ngrx update object inside array

I have a ngrx store with array of objects. What I am looking for is, update(modify) the object inside the array using the array index. My ngrx data will look like,

    policies: {
        beneficiaries: {
            beneficiaries: [{
                    name: 'pqr'
                    age: 56
                },
                {
                    name: 'xyz'
                    age: 76
                }
            ]
        }
    }

I have to update the beneficiary name based on the array index. So I have implemented the following reducer function

    on(policiesActions.updateBeneficiaryPercentage, (state, action) => {
        return {
          ...state,
          beneficiaries: {
            ...state.beneficiaries,
            beneficiaries: {
              ...state.beneficiaries.beneficiaries,
              [action.index]: {
                ...state.beneficiaries.beneficiaries[action.index],
                name: action.value
              }
            }
          }
        };
      })

The issue with the above code is that after running this code the structure of my store is changing to

policies: {
    beneficiaries: {
        beneficiaries: {
            0: {
                name: 'pqr'
                age: 1000
            },
            1: {
                name: 'xyz'
                age: 76
            }
        }
    }
}

Please help me to fix the code so that I can update the value without altering the store structure.

Upvotes: 14

Views: 21927

Answers (4)

Darren Street
Darren Street

Reputation: 1830

This is an easy way without resorting to deep diving into the state object.

on(ProductActions.updateProductSuccess, (state, action): ProductState => {

  // define a constant and map over the existing state, inserting your new item,
  // then assign your new object as the new state.

  const updatedProducts = state.products.map(
  product => action.product.id === product.id ? action.product : product);
  return {
    ...state,
    products: updatedProducts
  
   };
}),

This assumes you are passing a complete product object in your success action.

Upvotes: 8

Fabio Rizzello
Fabio Rizzello

Reputation: 51

You can use immer.js to generate a new state without touching the previous state. No need to roll your own solution, plus recreating the object by cloning the previous one manually is error prone.

import produce from "immer"

on(policiesActions.updateBeneficiaryPercentage, (state, action) => {
  return produce(state, draftState => {
    draftState.policies.beneficiaries.beneficiaries[action.index].name = action.value
  })
})

If you want to do it in a functional way you may try lenses example using ramda

Upvotes: 4

Józef Podlecki
Józef Podlecki

Reputation: 11283

When updating an object in array, I would re-create an array with all excluded objects and append last object which needs updated value.

const policies = {
  beneficiaries: {
    beneficiaries: [{
        name: 'pqr',
        age: 56
      },
      {
        name: 'xyz',
        age: 76
      }
    ]
  }
}

const updateObject = (state, action) => {

  if(!state.beneficiaries.beneficiaries[action.index]) {
    return state;
  }

  return {
    ...state,
    beneficiaries: {
      ...state.beneficiaries,
      beneficiaries: [
        ...state.beneficiaries.beneficiaries.slice(0, action.index),
        {
          ...state.beneficiaries.beneficiaries[action.index],
          name: action.value
        },
        ...state.beneficiaries.beneficiaries.slice(action.index + 1)
      ]
    }
  };
}

console.log(updateObject(policies, {index: 1, value: 'test'}))

--Edit

Added the snippet and changed the logic so it doesnt change list order.

Upvotes: 10

timdeschryver
timdeschryver

Reputation: 15505

Use the Array.map method:

arr.map((value, index) => index === action.index ? {...value, name: action.value} : value) 

Or just use ngrx-etc, which lets you mutate your state in a mutable way while remaining immutable

mutableOn(onAction, (state, action) => {
  state.arr[action.index].name = action.name
  return state
})

Upvotes: 18

Related Questions