Adding a child object to a normalized State with NgRx

106 Views Asked by At

I'm currently working on a businessPartnerEditor Feature for an angular frontend. I'm getting a nested businessPartnerComplete object from my backend.

//simplified object as received from backend:
{
  id:number,
  globalId:string,
  ...,
  addressList: [Address]
}
//with Address-Objects looking a little like this
{
  id: number,
  globalId:string,
  ...,
  toTheAttentionOf: [ToTheAttentionOf]
}

I used normalizr to normalize said object and entity adapter to build my state, leaving me with 3 FeatureStates, one for BusinessPartner, one for Address and one for ToTheAttentionOf.

//This is what my AddressFeatureState looks like:
{
  ids:[string],
  entities: Dictionary<AddressNormalized>,
  selectedAddressId: string
}
//and AddressNormalized:
{
  id:number,
  globalId:string,
  ...,
  toTheAttentionOf: [string]
}

Now I want my user to be able to create a new ToTheAttentionOf object under an address of their choice.

First I create a new ToTheAttentionOf object, then I get a globalId from the backend, set it on the object and finally I update my ToTheAttentionOfFeatureState by using entity adapter.upsert. This works as expected. But I still need to add the globalId of my new ToTheAttentionOf object to the parent Address in AddressState.

Here is my reducer for this:

export const AddressReducer = createReducer(
  on(addAttentionOf, (state, {toTheAttentionOf}) => {
    let entities = adapter.getSelectors().selectEntities(state);
    entities[state.selectedId].toTheAttentionOfDtos.push(toTheAttentionOf.globalId);
    return {...state, entities};
  })
)

But push throws a "TypeError: Cannot add property 1, object is not extensible", since (I believe, please correct any wrong notions) selectEntities(state) is returning part of the state, which is immutable.

So what would be the best way to go about this? I already read about creating a deepcopy, but is this the best and/or only way? This seems like a pretty common scenario, and yet it seems to be more trouble than it should. Should I have designed my state differently?

1

There are 1 best solutions below

0
Alexander Koslowski On

I solved it. I just didn't remember how to access a dictionary with the spread operator.

export const AddressReducer = createReducer(
  on(addAttentionOf, (state, {toTheAttentionOf}) => {
    return {
      ...state,
      entities: {
          ...state.entities,
          [state.selectedId]: {
              ...(state.entities[state.selectedId]),
              toTheAttentionOfDtos: [...state.entities[state.ids[0]].toTheAttentionOfDtos, toTheAttentionOf.globalId]
          }
      }
  })
)

But I would still appreciate it, if someone had a better solution. Doesn't feel like a good reducer. Too much indentation. If someone could give me any pointers on how to improve my store architecture, I'll gladly take the advice.