14

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.

4 Answers 4

18
+50

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
})
Sign up to request clarification or add additional context in comments.

2 Comments

ngrx-etc is an awesome library, sparing the developer from writing cumbersome code in some cases. This is particularly useful when items order in the array should not be altered. Thanks.
Just to note that ngrx-etc has been superceeded by ngrx-immer ( github.com/timdeschryver/ngrx-immer )
10

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.

2 Comments

This one is working... but the order of array elements are changing... That is the updated element will become the last element in the array...
Updated an answer. Yeah that didn't make sense at all when new state changes the order of list.
8

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.

Comments

4

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.